Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest similar names for types #96839

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this?

#[derive(HashStable_Generic)]
pub enum PrimTy {
Int(IntTy),
Expand Down
112 changes: 101 additions & 11 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -1825,6 +1825,76 @@ impl<'a> Resolver<'a> {
}
}

fn find_similarly_named_type(
&mut self,
ident: Symbol,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
) -> Option<TypoSuggestion> {
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() {
Comment on lines +1853 to +1857
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented the logics after this line with reference to the following function:

fn lookup_typo_candidate(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way these could be merged or share logic? Might be worth leaving a comment at the very least.

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::<Vec<Symbol>>();

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],
Expand Down Expand Up @@ -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)
Expand All @@ -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"),
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
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)
}
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/generic-associated-types/equality-bound.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ error[E0433]: failed to resolve: use of undeclared type `I`
--> $DIR/equality-bound.rs:9:41
|
LL | fn sum3<J: Iterator>(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

Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/macros/builtin-prelude-no-accidents.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>;
| ^^^ 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

Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/macros/macro_path_as_generic_bound.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/pattern/pattern-error-continue.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/resolve/suggest-similar-type-name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
struct FooType {}

impl FooType {
fn bar() {}
}

fn main() {
FooTyp::bar()
//~^ ERROR failed to resolve: use of undeclared type `FooTyp`
}
12 changes: 12 additions & 0 deletions src/test/ui/resolve/suggest-similar-type-name.stderr
Original file line number Diff line number Diff line change
@@ -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`.
9 changes: 9 additions & 0 deletions src/test/ui/resolve/suggest-type-to-lowercase-path.rs
Original file line number Diff line number Diff line change
@@ -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`
}
12 changes: 12 additions & 0 deletions src/test/ui/resolve/suggest-type-to-lowercase-path.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down