diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 48a41c8bd245a..0e8bdfd32f8d9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2400,7 +2400,7 @@ impl<'hir> Ty<'hir> { } /// Not represented directly in the AST; referred to by name through a `ty_path`. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum PrimTy { Int(IntTy), diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index a820f700869b2..e6513525dcf0b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -28,7 +28,7 @@ use rustc_span::{BytePos, Span}; use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; -use crate::late::{PatternSource, Rib}; +use crate::late::{PatternSource, Rib, RibKind}; use crate::path_names_to_string; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize}; use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot}; @@ -1825,6 +1825,76 @@ impl<'a> Resolver<'a> { } } + fn find_similarly_named_type( + &mut self, + ident: Symbol, + ribs: Option<&PerNS>>>, + ) -> Option { + fn is_type_candidate(res: Res) -> bool { + matches!( + res, + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::OpaqueTy + | DefKind::ForeignTy, + _ + ) | Res::PrimTy(..) + | Res::SelfTy { .. } + ) + } + + let mut names = Vec::new(); + if let Some(ribs) = ribs { + // Search in lexical scope. + // Walk backwards up the ribs in scope and collect candidates. + for rib in ribs[TypeNS].iter().rev() { + for (ident, res) in &rib.bindings { + if is_type_candidate(*res) { + names.push(TypoSuggestion::typo_from_res(ident.name, *res)); + } + } + // Items in scope + if let RibKind::ModuleRibKind(module) = rib.kind { + // Items from this module + self.add_module_candidates(module, &mut names, &is_type_candidate); + + if let ModuleKind::Block(..) = module.kind { + // We can through blocks + } else { + // Items from the prelude + if !module.no_implicit_prelude { + if let Some(prelude) = self.prelude { + self.add_module_candidates(prelude, &mut names, &is_type_candidate); + } + } + break; + } + } + } + // Add primitive types + names.extend(PrimTy::ALL.iter().map(|prim_ty| { + TypoSuggestion::typo_from_res(prim_ty.name(), Res::PrimTy(*prim_ty)) + })) + } + + // Make sure the suggestion is deterministic. + names.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap()); + let symbols = names.iter().map(|sugg| sugg.candidate).collect::>(); + + match find_best_match_for_name(&symbols, ident, None) { + Some(sugg) if sugg == ident => None, + sugg => sugg, + } + .and_then(|sugg| names.into_iter().find(|name| name.candidate == sugg)) + } + pub(crate) fn report_path_resolution_error( &mut self, path: &[Segment], @@ -1990,7 +2060,17 @@ impl<'a> Resolver<'a> { Applicability::MaybeIncorrect, )) } else { - None + self.find_similarly_named_type(ident.name, ribs).map(|sugg| { + ( + vec![(ident.span, sugg.candidate.to_string())], + format!( + "there is {} {} with a similar name", + sugg.res.article(), + sugg.res.descr(), + ), + Applicability::MaybeIncorrect, + ) + }) }; (format!("use of undeclared type `{}`", ident), suggestion) @@ -2001,16 +2081,26 @@ impl<'a> Resolver<'a> { String::from("add `extern crate alloc` to use the `alloc` crate"), Applicability::MaybeIncorrect, )) + } else if let Some(sugg) = + self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module) + { + Some(( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + )) } else { - self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map( - |sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from("there is a crate or module with a similar name"), - Applicability::MaybeIncorrect, - ) - }, - ) + self.find_similarly_named_type(ident.name, ribs).map(|sugg| { + ( + vec![(ident.span, sugg.candidate.to_string())], + format!( + "there is {} {} with a similar name", + sugg.res.article(), + sugg.res.descr() + ), + Applicability::MaybeIncorrect, + ) + }) }; (format!("use of undeclared crate or module `{}`", ident), suggestion) } diff --git a/src/test/ui/generic-associated-types/equality-bound.stderr b/src/test/ui/generic-associated-types/equality-bound.stderr index d78f7a7fbcee1..ba17f97044b7b 100644 --- a/src/test/ui/generic-associated-types/equality-bound.stderr +++ b/src/test/ui/generic-associated-types/equality-bound.stderr @@ -36,7 +36,10 @@ error[E0433]: failed to resolve: use of undeclared type `I` --> $DIR/equality-bound.rs:9:41 | LL | fn sum3(i: J) -> i32 where I::Item = i32 { - | ^ use of undeclared type `I` + | ^ + | | + | use of undeclared type `I` + | help: there is a type parameter with a similar name: `J` error: aborting due to 4 previous errors diff --git a/src/test/ui/macros/builtin-prelude-no-accidents.stderr b/src/test/ui/macros/builtin-prelude-no-accidents.stderr index 56af618d484b5..858cf7c7649cf 100644 --- a/src/test/ui/macros/builtin-prelude-no-accidents.stderr +++ b/src/test/ui/macros/builtin-prelude-no-accidents.stderr @@ -14,7 +14,10 @@ error[E0433]: failed to resolve: use of undeclared crate or module `vec` --> $DIR/builtin-prelude-no-accidents.rs:7:14 | LL | type B = vec::Vec; - | ^^^ use of undeclared crate or module `vec` + | ^^^ + | | + | use of undeclared crate or module `vec` + | help: there is a struct with a similar name (notice the capitalization): `Vec` error: aborting due to 3 previous errors diff --git a/src/test/ui/macros/macro_path_as_generic_bound.stderr b/src/test/ui/macros/macro_path_as_generic_bound.stderr index 00d954d24f380..280633518b72a 100644 --- a/src/test/ui/macros/macro_path_as_generic_bound.stderr +++ b/src/test/ui/macros/macro_path_as_generic_bound.stderr @@ -2,7 +2,10 @@ error[E0433]: failed to resolve: use of undeclared crate or module `m` --> $DIR/macro_path_as_generic_bound.rs:7:6 | LL | foo!(m::m2::A); - | ^ use of undeclared crate or module `m` + | ^ + | | + | use of undeclared crate or module `m` + | help: there is a type parameter with a similar name: `T` error: aborting due to previous error diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr index 4c2eff63ab5e8..742a73bd8ca7e 100644 --- a/src/test/ui/pattern/pattern-error-continue.stderr +++ b/src/test/ui/pattern/pattern-error-continue.stderr @@ -2,7 +2,10 @@ error[E0433]: failed to resolve: use of undeclared type `E` --> $DIR/pattern-error-continue.rs:33:9 | LL | E::V => {} - | ^ use of undeclared type `E` + | ^ + | | + | use of undeclared type `E` + | help: there is an enum with a similar name: `A` error[E0532]: expected tuple struct or tuple variant, found unit variant `A::D` --> $DIR/pattern-error-continue.rs:18:9 diff --git a/src/test/ui/resolve/suggest-similar-type-name.rs b/src/test/ui/resolve/suggest-similar-type-name.rs new file mode 100644 index 0000000000000..18c1210c9b534 --- /dev/null +++ b/src/test/ui/resolve/suggest-similar-type-name.rs @@ -0,0 +1,10 @@ +struct FooType {} + +impl FooType { + fn bar() {} +} + +fn main() { + FooTyp::bar() + //~^ ERROR failed to resolve: use of undeclared type `FooTyp` +} diff --git a/src/test/ui/resolve/suggest-similar-type-name.stderr b/src/test/ui/resolve/suggest-similar-type-name.stderr new file mode 100644 index 0000000000000..e5f27a899383b --- /dev/null +++ b/src/test/ui/resolve/suggest-similar-type-name.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: use of undeclared type `FooTyp` + --> $DIR/suggest-similar-type-name.rs:8:5 + | +LL | FooTyp::bar() + | ^^^^^^ + | | + | use of undeclared type `FooTyp` + | help: there is a struct with a similar name: `FooType` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/resolve/suggest-type-to-lowercase-path.rs b/src/test/ui/resolve/suggest-type-to-lowercase-path.rs new file mode 100644 index 0000000000000..1235a46367a4c --- /dev/null +++ b/src/test/ui/resolve/suggest-type-to-lowercase-path.rs @@ -0,0 +1,9 @@ +// Beginners often write an incorrect type name whose initial letter is +// a lowercase while the correct one is an uppercase. +// (e.g. `string` instead of `String`) +// This tests that we suggest the latter when we encounter the former. + +fn main() { + let _ = string::new(); + //~^ ERROR failed to resolve: use of undeclared crate or module `string` +} diff --git a/src/test/ui/resolve/suggest-type-to-lowercase-path.stderr b/src/test/ui/resolve/suggest-type-to-lowercase-path.stderr new file mode 100644 index 0000000000000..e37fe3ab74b48 --- /dev/null +++ b/src/test/ui/resolve/suggest-type-to-lowercase-path.stderr @@ -0,0 +1,12 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `string` + --> $DIR/suggest-type-to-lowercase-path.rs:7:13 + | +LL | let _ = string::new(); + | ^^^^^^ + | | + | use of undeclared crate or module `string` + | help: there is a struct with a similar name (notice the capitalization): `String` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/suggestions/type-ascription-instead-of-path.stderr b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr index 518660cfa1686..575f40168a6eb 100644 --- a/src/test/ui/suggestions/type-ascription-instead-of-path.stderr +++ b/src/test/ui/suggestions/type-ascription-instead-of-path.stderr @@ -2,7 +2,10 @@ error[E0433]: failed to resolve: use of undeclared crate or module `io` --> $DIR/type-ascription-instead-of-path.rs:2:9 | LL | std:io::stdin(); - | ^^ use of undeclared crate or module `io` + | ^^ + | | + | use of undeclared crate or module `io` + | help: there is a builtin type with a similar name: `i8` error[E0423]: expected value, found crate `std` --> $DIR/type-ascription-instead-of-path.rs:2:5