about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-09-23 04:29:16 +0200
committerGitHub <noreply@github.com>2022-09-23 04:29:16 +0200
commitc2d2535b8427845634d503cc8f75595d296f1268 (patch)
tree1b48523916436b22e318f5b383a60aff390425cd
parent5d7937de8cfeba357136bcd8b960f3d3984fe19a (diff)
parentc65866000e07270817465f3cbb8b1d01ecdb9a20 (diff)
downloadrust-c2d2535b8427845634d503cc8f75595d296f1268.tar.gz
rust-c2d2535b8427845634d503cc8f75595d296f1268.zip
Rollup merge of #101664 - mejrs:similarity, r=fee1-dead
Note if mismatched types have a similar name

If users get a type error between similarly named types, it will point out that these are actually different types, and where they were defined.
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs115
-rw-r--r--compiler/rustc_infer/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs31
-rw-r--r--src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr12
-rw-r--r--src/test/ui/mismatched_types/show_module.rs18
-rw-r--r--src/test/ui/mismatched_types/show_module.stderr23
-rw-r--r--src/test/ui/mismatched_types/similar_paths.rs11
-rw-r--r--src/test/ui/mismatched_types/similar_paths.stderr23
-rw-r--r--src/test/ui/mismatched_types/similar_paths_primitive.rs10
-rw-r--r--src/test/ui/mismatched_types/similar_paths_primitive.stderr24
-rw-r--r--src/test/ui/type/type-mismatch-same-crate-name.stderr11
11 files changed, 274 insertions, 5 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 18255a5089c..82099d9e3f3 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa
 
 use crate::infer;
 use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
+use crate::infer::ExpectedFound;
 use crate::traits::error_reporting::report_object_safety_error;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -1653,8 +1654,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 ),
                 Mismatch::Fixed(s) => (s.into(), s.into(), None),
             };
-            match (&terr, expected == found) {
-                (TypeError::Sorts(values), extra) => {
+
+            enum Similar<'tcx> {
+                Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> },
+                PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> },
+                PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> },
+            }
+
+            let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {
+                if let ty::Adt(expected, _) = expected.kind() && let Some(primitive) = found.primitive_symbol() {
+                    let path = self.tcx.def_path(expected.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveFound { expected: *expected, found });
+                    }
+                } else if let Some(primitive) = expected.primitive_symbol() && let ty::Adt(found, _) = found.kind() {
+                    let path = self.tcx.def_path(found.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveExpected { expected, found: *found });
+                    }
+                } else if let ty::Adt(expected, _) = expected.kind() && let ty::Adt(found, _) = found.kind() {
+                    if !expected.did().is_local() && expected.did().krate == found.did().krate {
+                        // Most likely types from different versions of the same crate
+                        // are in play, in which case this message isn't so helpful.
+                        // A "perhaps two different versions..." error is already emitted for that.
+                        return None;
+                    }
+                    let f_path = self.tcx.def_path(found.did()).data;
+                    let e_path = self.tcx.def_path(expected.did()).data;
+
+                    if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) && e_last ==  f_last {
+                        return Some(Similar::Adts{expected: *expected, found: *found});
+                    }
+                }
+                None
+            };
+
+            match terr {
+                // If two types mismatch but have similar names, mention that specifically.
+                TypeError::Sorts(values) if let Some(s) = similarity(values) => {
+                    let diagnose_primitive =
+                        |prim: Ty<'tcx>,
+                         shadow: Ty<'tcx>,
+                         defid: DefId,
+                         diagnostic: &mut Diagnostic| {
+                            let name = shadow.sort_string(self.tcx);
+                            diagnostic.note(format!(
+                            "{prim} and {name} have similar names, but are actually distinct types"
+                        ));
+                            diagnostic
+                                .note(format!("{prim} is a primitive defined by the language"));
+                            let def_span = self.tcx.def_span(defid);
+                            let msg = if defid.is_local() {
+                                format!("{name} is defined in the current crate")
+                            } else {
+                                let crate_name = self.tcx.crate_name(defid.krate);
+                                format!("{name} is defined in crate `{crate_name}")
+                            };
+                            diagnostic.span_note(def_span, msg);
+                        };
+
+                    let diagnose_adts =
+                        |expected_adt : ty::AdtDef<'tcx>,
+                         found_adt: ty::AdtDef<'tcx>,
+                         diagnostic: &mut Diagnostic| {
+                            let found_name = values.found.sort_string(self.tcx);
+                            let expected_name = values.expected.sort_string(self.tcx);
+
+                            let found_defid = found_adt.did();
+                            let expected_defid = expected_adt.did();
+
+                            diagnostic.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types"));
+                            for (defid, name) in
+                                [(found_defid, found_name), (expected_defid, expected_name)]
+                            {
+                                let def_span = self.tcx.def_span(defid);
+
+                                let msg = if found_defid.is_local() && expected_defid.is_local() {
+                                    let module = self
+                                        .tcx
+                                        .parent_module_from_def_id(defid.expect_local())
+                                        .to_def_id();
+                                    let module_name = self.tcx.def_path(module).to_string_no_crate_verbose();
+                                    format!("{name} is defined in module `crate{module_name}` of the current crate")
+                                } else if defid.is_local() {
+                                    format!("{name} is defined in the current crate")
+                                } else {
+                                    let crate_name = self.tcx.crate_name(defid.krate);
+                                    format!("{name} is defined in crate `{crate_name}`")
+                                };
+                                diagnostic.span_note(def_span, msg);
+                            }
+                        };
+
+                    match s {
+                        Similar::Adts{expected, found} => {
+                            diagnose_adts(expected, found, diag)
+                        }
+                        Similar::PrimitiveFound{expected, found: prim} => {
+                            diagnose_primitive(prim, values.expected, expected.did(), diag)
+                        }
+                        Similar::PrimitiveExpected{expected: prim, found} => {
+                            diagnose_primitive(prim, values.found, found.did(), diag)
+                        }
+                    }
+                }
+                TypeError::Sorts(values) => {
+                    let extra = expected == found;
                     let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
                         (true, ty::Opaque(def_id, _)) => {
                             let sm = self.tcx.sess.source_map();
@@ -1707,10 +1814,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         );
                     }
                 }
-                (TypeError::ObjectUnsafeCoercion(_), _) => {
+                TypeError::ObjectUnsafeCoercion(_) => {
                     diag.note_unsuccessful_coercion(found, expected);
                 }
-                (_, _) => {
+                _ => {
                     debug!(
                         "note_type_err: exp_found={:?}, expected={:?} found={:?}",
                         exp_found, expected, found
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index ef60d2c9188..909a597e221 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -20,6 +20,7 @@
 #![cfg_attr(bootstrap, feature(label_break_value))]
 #![feature(let_chains)]
 #![cfg_attr(bootstrap, feature(let_else))]
+#![feature(if_let_guard)]
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index d2f73f274f8..e773b3032aa 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -19,7 +19,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_index::vec::Idx;
 use rustc_macros::HashStable;
-use rustc_span::symbol::{kw, Symbol};
+use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi;
 use std::borrow::Cow;
@@ -2207,6 +2207,35 @@ impl<'tcx> Ty<'tcx> {
             }
         }
     }
+
+    // If `self` is a primitive, return its [`Symbol`].
+    pub fn primitive_symbol(self) -> Option<Symbol> {
+        match self.kind() {
+            ty::Bool => Some(sym::bool),
+            ty::Char => Some(sym::char),
+            ty::Float(f) => match f {
+                ty::FloatTy::F32 => Some(sym::f32),
+                ty::FloatTy::F64 => Some(sym::f64),
+            },
+            ty::Int(f) => match f {
+                ty::IntTy::Isize => Some(sym::isize),
+                ty::IntTy::I8 => Some(sym::i8),
+                ty::IntTy::I16 => Some(sym::i16),
+                ty::IntTy::I32 => Some(sym::i32),
+                ty::IntTy::I64 => Some(sym::i64),
+                ty::IntTy::I128 => Some(sym::i128),
+            },
+            ty::Uint(f) => match f {
+                ty::UintTy::Usize => Some(sym::usize),
+                ty::UintTy::U8 => Some(sym::u8),
+                ty::UintTy::U16 => Some(sym::u16),
+                ty::UintTy::U32 => Some(sym::u32),
+                ty::UintTy::U64 => Some(sym::u64),
+                ty::UintTy::U128 => Some(sym::u128),
+            },
+            _ => None,
+        }
+    }
 }
 
 /// Extra information about why we ended up with a particular variance.
diff --git a/src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr b/src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr
index aed7f72c660..a8f23f81dea 100644
--- a/src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr
+++ b/src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr
@@ -5,6 +5,18 @@ LL | fn bar(x: x::Foo) -> y::Foo {
    |                      ------ expected `y::Foo` because of return type
 LL |     return x;
    |            ^ expected enum `y::Foo`, found enum `x::Foo`
+   |
+   = note: enum `x::Foo` and enum `y::Foo` have similar names, but are actually distinct types
+note: enum `x::Foo` is defined in module `crate::x` of the current crate
+  --> $DIR/fully-qualified-type-name2.rs:4:5
+   |
+LL |     pub enum Foo { }
+   |     ^^^^^^^^^^^^
+note: enum `y::Foo` is defined in module `crate::y` of the current crate
+  --> $DIR/fully-qualified-type-name2.rs:8:5
+   |
+LL |     pub enum Foo { }
+   |     ^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/mismatched_types/show_module.rs b/src/test/ui/mismatched_types/show_module.rs
new file mode 100644
index 00000000000..61550b88755
--- /dev/null
+++ b/src/test/ui/mismatched_types/show_module.rs
@@ -0,0 +1,18 @@
+pub mod blah {
+    pub mod baz {
+        pub struct Foo;
+    }
+}
+
+pub mod meh {
+    pub struct Foo;
+}
+
+pub type Foo = blah::baz::Foo;
+
+fn foo() -> Foo {
+    meh::Foo
+    //~^ ERROR mismatched types [E0308]
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/show_module.stderr b/src/test/ui/mismatched_types/show_module.stderr
new file mode 100644
index 00000000000..5e48e0955aa
--- /dev/null
+++ b/src/test/ui/mismatched_types/show_module.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/show_module.rs:14:5
+   |
+LL | fn foo() -> Foo {
+   |             --- expected `baz::Foo` because of return type
+LL |     meh::Foo
+   |     ^^^^^^^^ expected struct `baz::Foo`, found struct `meh::Foo`
+   |
+   = note: struct `meh::Foo` and struct `baz::Foo` have similar names, but are actually distinct types
+note: struct `meh::Foo` is defined in module `crate::meh` of the current crate
+  --> $DIR/show_module.rs:8:5
+   |
+LL |     pub struct Foo;
+   |     ^^^^^^^^^^^^^^
+note: struct `baz::Foo` is defined in module `crate::blah::baz` of the current crate
+  --> $DIR/show_module.rs:3:9
+   |
+LL |         pub struct Foo;
+   |         ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/mismatched_types/similar_paths.rs b/src/test/ui/mismatched_types/similar_paths.rs
new file mode 100644
index 00000000000..4b9157f39bf
--- /dev/null
+++ b/src/test/ui/mismatched_types/similar_paths.rs
@@ -0,0 +1,11 @@
+enum Option<T> {
+    Some(T),
+    None,
+}
+
+pub fn foo() -> Option<u8> {
+    Some(42_u8)
+    //~^ ERROR mismatched types [E0308]
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/similar_paths.stderr b/src/test/ui/mismatched_types/similar_paths.stderr
new file mode 100644
index 00000000000..e65ae58d4ce
--- /dev/null
+++ b/src/test/ui/mismatched_types/similar_paths.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/similar_paths.rs:7:5
+   |
+LL | pub fn foo() -> Option<u8> {
+   |                 ---------- expected `Option<u8>` because of return type
+LL |     Some(42_u8)
+   |     ^^^^^^^^^^^ expected enum `Option`, found enum `std::option::Option`
+   |
+   = note: enum `std::option::Option` and enum `Option` have similar names, but are actually distinct types
+note: enum `std::option::Option` is defined in crate `core`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+   |
+LL | pub enum Option<T> {
+   | ^^^^^^^^^^^^^^^^^^
+note: enum `Option` is defined in the current crate
+  --> $DIR/similar_paths.rs:1:1
+   |
+LL | enum Option<T> {
+   | ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/mismatched_types/similar_paths_primitive.rs b/src/test/ui/mismatched_types/similar_paths_primitive.rs
new file mode 100644
index 00000000000..8f5b7cce469
--- /dev/null
+++ b/src/test/ui/mismatched_types/similar_paths_primitive.rs
@@ -0,0 +1,10 @@
+#![allow(non_camel_case_types)]
+
+struct bool;
+
+fn foo(_: bool) {}
+
+fn main() {
+    foo(true);
+    //~^ ERROR mismatched types [E0308]
+}
diff --git a/src/test/ui/mismatched_types/similar_paths_primitive.stderr b/src/test/ui/mismatched_types/similar_paths_primitive.stderr
new file mode 100644
index 00000000000..8a2f73945e8
--- /dev/null
+++ b/src/test/ui/mismatched_types/similar_paths_primitive.stderr
@@ -0,0 +1,24 @@
+error[E0308]: mismatched types
+  --> $DIR/similar_paths_primitive.rs:8:9
+   |
+LL |     foo(true);
+   |     --- ^^^^ expected struct `bool`, found `bool`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: bool and struct `bool` have similar names, but are actually distinct types
+   = note: bool is a primitive defined by the language
+note: struct `bool` is defined in the current crate
+  --> $DIR/similar_paths_primitive.rs:3:1
+   |
+LL | struct bool;
+   | ^^^^^^^^^^^
+note: function defined here
+  --> $DIR/similar_paths_primitive.rs:5:4
+   |
+LL | fn foo(_: bool) {}
+   |    ^^^ -------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/type/type-mismatch-same-crate-name.stderr b/src/test/ui/type/type-mismatch-same-crate-name.stderr
index 783f747fa6d..fcafd315ebf 100644
--- a/src/test/ui/type/type-mismatch-same-crate-name.stderr
+++ b/src/test/ui/type/type-mismatch-same-crate-name.stderr
@@ -6,6 +6,17 @@ LL |         a::try_foo(foo2);
    |         |
    |         arguments to this function are incorrect
    |
+   = note: struct `main::a::Foo` and struct `main::a::Foo` have similar names, but are actually distinct types
+note: struct `main::a::Foo` is defined in crate `crate_a2`
+  --> $DIR/auxiliary/crate_a2.rs:1:1
+   |
+LL | pub struct Foo;
+   | ^^^^^^^^^^^^^^
+note: struct `main::a::Foo` is defined in crate `crate_a1`
+  --> $DIR/auxiliary/crate_a1.rs:1:1
+   |
+LL | pub struct Foo;
+   | ^^^^^^^^^^^^^^
    = note: perhaps two different versions of crate `crate_a1` are being used?
 note: function defined here
   --> $DIR/auxiliary/crate_a1.rs:10:8