Skip to content

Commit c2d2535

Browse files
authored
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.
2 parents 5d7937d + c658660 commit c2d2535

File tree

11 files changed

+274
-5
lines changed

11 files changed

+274
-5
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+111-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa
5151

5252
use crate::infer;
5353
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
54+
use crate::infer::ExpectedFound;
5455
use crate::traits::error_reporting::report_object_safety_error;
5556
use crate::traits::{
5657
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -1653,8 +1654,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16531654
),
16541655
Mismatch::Fixed(s) => (s.into(), s.into(), None),
16551656
};
1656-
match (&terr, expected == found) {
1657-
(TypeError::Sorts(values), extra) => {
1657+
1658+
enum Similar<'tcx> {
1659+
Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> },
1660+
PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> },
1661+
PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> },
1662+
}
1663+
1664+
let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {
1665+
if let ty::Adt(expected, _) = expected.kind() && let Some(primitive) = found.primitive_symbol() {
1666+
let path = self.tcx.def_path(expected.did()).data;
1667+
let name = path.last().unwrap().data.get_opt_name();
1668+
if name == Some(primitive) {
1669+
return Some(Similar::PrimitiveFound { expected: *expected, found });
1670+
}
1671+
} else if let Some(primitive) = expected.primitive_symbol() && let ty::Adt(found, _) = found.kind() {
1672+
let path = self.tcx.def_path(found.did()).data;
1673+
let name = path.last().unwrap().data.get_opt_name();
1674+
if name == Some(primitive) {
1675+
return Some(Similar::PrimitiveExpected { expected, found: *found });
1676+
}
1677+
} else if let ty::Adt(expected, _) = expected.kind() && let ty::Adt(found, _) = found.kind() {
1678+
if !expected.did().is_local() && expected.did().krate == found.did().krate {
1679+
// Most likely types from different versions of the same crate
1680+
// are in play, in which case this message isn't so helpful.
1681+
// A "perhaps two different versions..." error is already emitted for that.
1682+
return None;
1683+
}
1684+
let f_path = self.tcx.def_path(found.did()).data;
1685+
let e_path = self.tcx.def_path(expected.did()).data;
1686+
1687+
if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) && e_last == f_last {
1688+
return Some(Similar::Adts{expected: *expected, found: *found});
1689+
}
1690+
}
1691+
None
1692+
};
1693+
1694+
match terr {
1695+
// If two types mismatch but have similar names, mention that specifically.
1696+
TypeError::Sorts(values) if let Some(s) = similarity(values) => {
1697+
let diagnose_primitive =
1698+
|prim: Ty<'tcx>,
1699+
shadow: Ty<'tcx>,
1700+
defid: DefId,
1701+
diagnostic: &mut Diagnostic| {
1702+
let name = shadow.sort_string(self.tcx);
1703+
diagnostic.note(format!(
1704+
"{prim} and {name} have similar names, but are actually distinct types"
1705+
));
1706+
diagnostic
1707+
.note(format!("{prim} is a primitive defined by the language"));
1708+
let def_span = self.tcx.def_span(defid);
1709+
let msg = if defid.is_local() {
1710+
format!("{name} is defined in the current crate")
1711+
} else {
1712+
let crate_name = self.tcx.crate_name(defid.krate);
1713+
format!("{name} is defined in crate `{crate_name}")
1714+
};
1715+
diagnostic.span_note(def_span, msg);
1716+
};
1717+
1718+
let diagnose_adts =
1719+
|expected_adt : ty::AdtDef<'tcx>,
1720+
found_adt: ty::AdtDef<'tcx>,
1721+
diagnostic: &mut Diagnostic| {
1722+
let found_name = values.found.sort_string(self.tcx);
1723+
let expected_name = values.expected.sort_string(self.tcx);
1724+
1725+
let found_defid = found_adt.did();
1726+
let expected_defid = expected_adt.did();
1727+
1728+
diagnostic.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types"));
1729+
for (defid, name) in
1730+
[(found_defid, found_name), (expected_defid, expected_name)]
1731+
{
1732+
let def_span = self.tcx.def_span(defid);
1733+
1734+
let msg = if found_defid.is_local() && expected_defid.is_local() {
1735+
let module = self
1736+
.tcx
1737+
.parent_module_from_def_id(defid.expect_local())
1738+
.to_def_id();
1739+
let module_name = self.tcx.def_path(module).to_string_no_crate_verbose();
1740+
format!("{name} is defined in module `crate{module_name}` of the current crate")
1741+
} else if defid.is_local() {
1742+
format!("{name} is defined in the current crate")
1743+
} else {
1744+
let crate_name = self.tcx.crate_name(defid.krate);
1745+
format!("{name} is defined in crate `{crate_name}`")
1746+
};
1747+
diagnostic.span_note(def_span, msg);
1748+
}
1749+
};
1750+
1751+
match s {
1752+
Similar::Adts{expected, found} => {
1753+
diagnose_adts(expected, found, diag)
1754+
}
1755+
Similar::PrimitiveFound{expected, found: prim} => {
1756+
diagnose_primitive(prim, values.expected, expected.did(), diag)
1757+
}
1758+
Similar::PrimitiveExpected{expected: prim, found} => {
1759+
diagnose_primitive(prim, values.found, found.did(), diag)
1760+
}
1761+
}
1762+
}
1763+
TypeError::Sorts(values) => {
1764+
let extra = expected == found;
16581765
let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
16591766
(true, ty::Opaque(def_id, _)) => {
16601767
let sm = self.tcx.sess.source_map();
@@ -1707,10 +1814,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17071814
);
17081815
}
17091816
}
1710-
(TypeError::ObjectUnsafeCoercion(_), _) => {
1817+
TypeError::ObjectUnsafeCoercion(_) => {
17111818
diag.note_unsuccessful_coercion(found, expected);
17121819
}
1713-
(_, _) => {
1820+
_ => {
17141821
debug!(
17151822
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
17161823
exp_found, expected, found

compiler/rustc_infer/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![cfg_attr(bootstrap, feature(label_break_value))]
2121
#![feature(let_chains)]
2222
#![cfg_attr(bootstrap, feature(let_else))]
23+
#![feature(if_let_guard)]
2324
#![feature(min_specialization)]
2425
#![feature(never_type)]
2526
#![feature(try_blocks)]

compiler/rustc_middle/src/ty/sty.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_hir as hir;
1919
use rustc_hir::def_id::DefId;
2020
use rustc_index::vec::Idx;
2121
use rustc_macros::HashStable;
22-
use rustc_span::symbol::{kw, Symbol};
22+
use rustc_span::symbol::{kw, sym, Symbol};
2323
use rustc_target::abi::VariantIdx;
2424
use rustc_target::spec::abi;
2525
use std::borrow::Cow;
@@ -2207,6 +2207,35 @@ impl<'tcx> Ty<'tcx> {
22072207
}
22082208
}
22092209
}
2210+
2211+
// If `self` is a primitive, return its [`Symbol`].
2212+
pub fn primitive_symbol(self) -> Option<Symbol> {
2213+
match self.kind() {
2214+
ty::Bool => Some(sym::bool),
2215+
ty::Char => Some(sym::char),
2216+
ty::Float(f) => match f {
2217+
ty::FloatTy::F32 => Some(sym::f32),
2218+
ty::FloatTy::F64 => Some(sym::f64),
2219+
},
2220+
ty::Int(f) => match f {
2221+
ty::IntTy::Isize => Some(sym::isize),
2222+
ty::IntTy::I8 => Some(sym::i8),
2223+
ty::IntTy::I16 => Some(sym::i16),
2224+
ty::IntTy::I32 => Some(sym::i32),
2225+
ty::IntTy::I64 => Some(sym::i64),
2226+
ty::IntTy::I128 => Some(sym::i128),
2227+
},
2228+
ty::Uint(f) => match f {
2229+
ty::UintTy::Usize => Some(sym::usize),
2230+
ty::UintTy::U8 => Some(sym::u8),
2231+
ty::UintTy::U16 => Some(sym::u16),
2232+
ty::UintTy::U32 => Some(sym::u32),
2233+
ty::UintTy::U64 => Some(sym::u64),
2234+
ty::UintTy::U128 => Some(sym::u128),
2235+
},
2236+
_ => None,
2237+
}
2238+
}
22102239
}
22112240

22122241
/// Extra information about why we ended up with a particular variance.

src/test/ui/fully-qualified-type/fully-qualified-type-name2.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ LL | fn bar(x: x::Foo) -> y::Foo {
55
| ------ expected `y::Foo` because of return type
66
LL | return x;
77
| ^ expected enum `y::Foo`, found enum `x::Foo`
8+
|
9+
= note: enum `x::Foo` and enum `y::Foo` have similar names, but are actually distinct types
10+
note: enum `x::Foo` is defined in module `crate::x` of the current crate
11+
--> $DIR/fully-qualified-type-name2.rs:4:5
12+
|
13+
LL | pub enum Foo { }
14+
| ^^^^^^^^^^^^
15+
note: enum `y::Foo` is defined in module `crate::y` of the current crate
16+
--> $DIR/fully-qualified-type-name2.rs:8:5
17+
|
18+
LL | pub enum Foo { }
19+
| ^^^^^^^^^^^^
820

921
error: aborting due to previous error
1022

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pub mod blah {
2+
pub mod baz {
3+
pub struct Foo;
4+
}
5+
}
6+
7+
pub mod meh {
8+
pub struct Foo;
9+
}
10+
11+
pub type Foo = blah::baz::Foo;
12+
13+
fn foo() -> Foo {
14+
meh::Foo
15+
//~^ ERROR mismatched types [E0308]
16+
}
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/show_module.rs:14:5
3+
|
4+
LL | fn foo() -> Foo {
5+
| --- expected `baz::Foo` because of return type
6+
LL | meh::Foo
7+
| ^^^^^^^^ expected struct `baz::Foo`, found struct `meh::Foo`
8+
|
9+
= note: struct `meh::Foo` and struct `baz::Foo` have similar names, but are actually distinct types
10+
note: struct `meh::Foo` is defined in module `crate::meh` of the current crate
11+
--> $DIR/show_module.rs:8:5
12+
|
13+
LL | pub struct Foo;
14+
| ^^^^^^^^^^^^^^
15+
note: struct `baz::Foo` is defined in module `crate::blah::baz` of the current crate
16+
--> $DIR/show_module.rs:3:9
17+
|
18+
LL | pub struct Foo;
19+
| ^^^^^^^^^^^^^^
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum Option<T> {
2+
Some(T),
3+
None,
4+
}
5+
6+
pub fn foo() -> Option<u8> {
7+
Some(42_u8)
8+
//~^ ERROR mismatched types [E0308]
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/similar_paths.rs:7:5
3+
|
4+
LL | pub fn foo() -> Option<u8> {
5+
| ---------- expected `Option<u8>` because of return type
6+
LL | Some(42_u8)
7+
| ^^^^^^^^^^^ expected enum `Option`, found enum `std::option::Option`
8+
|
9+
= note: enum `std::option::Option` and enum `Option` have similar names, but are actually distinct types
10+
note: enum `std::option::Option` is defined in crate `core`
11+
--> $SRC_DIR/core/src/option.rs:LL:COL
12+
|
13+
LL | pub enum Option<T> {
14+
| ^^^^^^^^^^^^^^^^^^
15+
note: enum `Option` is defined in the current crate
16+
--> $DIR/similar_paths.rs:1:1
17+
|
18+
LL | enum Option<T> {
19+
| ^^^^^^^^^^^^^^
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![allow(non_camel_case_types)]
2+
3+
struct bool;
4+
5+
fn foo(_: bool) {}
6+
7+
fn main() {
8+
foo(true);
9+
//~^ ERROR mismatched types [E0308]
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/similar_paths_primitive.rs:8:9
3+
|
4+
LL | foo(true);
5+
| --- ^^^^ expected struct `bool`, found `bool`
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
= note: bool and struct `bool` have similar names, but are actually distinct types
10+
= note: bool is a primitive defined by the language
11+
note: struct `bool` is defined in the current crate
12+
--> $DIR/similar_paths_primitive.rs:3:1
13+
|
14+
LL | struct bool;
15+
| ^^^^^^^^^^^
16+
note: function defined here
17+
--> $DIR/similar_paths_primitive.rs:5:4
18+
|
19+
LL | fn foo(_: bool) {}
20+
| ^^^ -------
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/type/type-mismatch-same-crate-name.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ LL | a::try_foo(foo2);
66
| |
77
| arguments to this function are incorrect
88
|
9+
= note: struct `main::a::Foo` and struct `main::a::Foo` have similar names, but are actually distinct types
10+
note: struct `main::a::Foo` is defined in crate `crate_a2`
11+
--> $DIR/auxiliary/crate_a2.rs:1:1
12+
|
13+
LL | pub struct Foo;
14+
| ^^^^^^^^^^^^^^
15+
note: struct `main::a::Foo` is defined in crate `crate_a1`
16+
--> $DIR/auxiliary/crate_a1.rs:1:1
17+
|
18+
LL | pub struct Foo;
19+
| ^^^^^^^^^^^^^^
920
= note: perhaps two different versions of crate `crate_a1` are being used?
1021
note: function defined here
1122
--> $DIR/auxiliary/crate_a1.rs:10:8

0 commit comments

Comments
 (0)