diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 67526c2228937..aba2447fa68e9 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>| { + 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 ef60d2c918845..909a597e22135 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 36e5608503904..6583e61931a53 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; @@ -2274,6 +2274,35 @@ impl<'tcx> Ty<'tcx> { } } } + + // If `self` is a primitive, return its [`Symbol`]. + pub fn primitive_symbol(self) -> Option { + 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 aed7f72c660df..a8f23f81dea02 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 0000000000000..61550b8875520 --- /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 0000000000000..5e48e0955aae9 --- /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 0000000000000..4b9157f39bf48 --- /dev/null +++ b/src/test/ui/mismatched_types/similar_paths.rs @@ -0,0 +1,11 @@ +enum Option { + Some(T), + None, +} + +pub fn foo() -> Option { + 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 0000000000000..e65ae58d4ce96 --- /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 { + | ---------- expected `Option` 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 { + | ^^^^^^^^^^^^^^^^^^ +note: enum `Option` is defined in the current crate + --> $DIR/similar_paths.rs:1:1 + | +LL | enum Option { + | ^^^^^^^^^^^^^^ + +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 0000000000000..8f5b7cce46908 --- /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 0000000000000..8a2f73945e841 --- /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 783f747fa6db6..fcafd315ebf54 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