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.
This commit is contained in:
commit
c2d2535b84
11 changed files with 274 additions and 5 deletions
|
@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa
|
||||||
|
|
||||||
use crate::infer;
|
use crate::infer;
|
||||||
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
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::error_reporting::report_object_safety_error;
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||||
|
@ -1653,8 +1654,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
),
|
),
|
||||||
Mismatch::Fixed(s) => (s.into(), s.into(), None),
|
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()) {
|
let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
|
||||||
(true, ty::Opaque(def_id, _)) => {
|
(true, ty::Opaque(def_id, _)) => {
|
||||||
let sm = self.tcx.sess.source_map();
|
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);
|
diag.note_unsuccessful_coercion(found, expected);
|
||||||
}
|
}
|
||||||
(_, _) => {
|
_ => {
|
||||||
debug!(
|
debug!(
|
||||||
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
|
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
|
||||||
exp_found, expected, found
|
exp_found, expected, found
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#![cfg_attr(bootstrap, feature(label_break_value))]
|
#![cfg_attr(bootstrap, feature(label_break_value))]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![cfg_attr(bootstrap, feature(let_else))]
|
#![cfg_attr(bootstrap, feature(let_else))]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
|
|
@ -19,7 +19,7 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_index::vec::Idx;
|
use rustc_index::vec::Idx;
|
||||||
use rustc_macros::HashStable;
|
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::abi::VariantIdx;
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
use std::borrow::Cow;
|
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.
|
/// Extra information about why we ended up with a particular variance.
|
||||||
|
|
|
@ -5,6 +5,18 @@ LL | fn bar(x: x::Foo) -> y::Foo {
|
||||||
| ------ expected `y::Foo` because of return type
|
| ------ expected `y::Foo` because of return type
|
||||||
LL | return x;
|
LL | return x;
|
||||||
| ^ expected enum `y::Foo`, found enum `x::Foo`
|
| ^ 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
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
18
src/test/ui/mismatched_types/show_module.rs
Normal file
18
src/test/ui/mismatched_types/show_module.rs
Normal file
|
@ -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() {}
|
23
src/test/ui/mismatched_types/show_module.stderr
Normal file
23
src/test/ui/mismatched_types/show_module.stderr
Normal file
|
@ -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`.
|
11
src/test/ui/mismatched_types/similar_paths.rs
Normal file
11
src/test/ui/mismatched_types/similar_paths.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
enum Option<T> {
|
||||||
|
Some(T),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foo() -> Option<u8> {
|
||||||
|
Some(42_u8)
|
||||||
|
//~^ ERROR mismatched types [E0308]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
23
src/test/ui/mismatched_types/similar_paths.stderr
Normal file
23
src/test/ui/mismatched_types/similar_paths.stderr
Normal file
|
@ -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`.
|
10
src/test/ui/mismatched_types/similar_paths_primitive.rs
Normal file
10
src/test/ui/mismatched_types/similar_paths_primitive.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
struct bool;
|
||||||
|
|
||||||
|
fn foo(_: bool) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(true);
|
||||||
|
//~^ ERROR mismatched types [E0308]
|
||||||
|
}
|
24
src/test/ui/mismatched_types/similar_paths_primitive.stderr
Normal file
24
src/test/ui/mismatched_types/similar_paths_primitive.stderr
Normal file
|
@ -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`.
|
|
@ -6,6 +6,17 @@ LL | a::try_foo(foo2);
|
||||||
| |
|
| |
|
||||||
| arguments to this function are incorrect
|
| 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: perhaps two different versions of crate `crate_a1` are being used?
|
||||||
note: function defined here
|
note: function defined here
|
||||||
--> $DIR/auxiliary/crate_a1.rs:10:8
|
--> $DIR/auxiliary/crate_a1.rs:10:8
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue