From 6d947e6d489269124067fb91504f25f61187858e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 16 Oct 2022 08:51:11 +0000 Subject: Account for hygiene when suggesting typos. --- compiler/rustc_resolve/src/diagnostics.rs | 13 ++++++++----- compiler/rustc_resolve/src/late/diagnostics.rs | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c4644d4f076..5d868ebec94 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,7 +24,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, SyntaxContext}; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::late::{PatternSource, Rib}; @@ -47,6 +47,7 @@ pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); /// similarly named label and whether or not it is reachable. pub(crate) type LabelSuggestion = (Ident, bool); +#[derive(Debug)] pub(crate) enum SuggestionTarget { /// The target has a similar name as the name used by the programmer (probably a typo) SimilarlyNamed, @@ -54,6 +55,7 @@ pub(crate) enum SuggestionTarget { SingleItem, } +#[derive(Debug)] pub(crate) struct TypoSuggestion { pub candidate: Symbol, pub res: Res, @@ -482,11 +484,12 @@ impl<'a> Resolver<'a> { module: Module<'a>, names: &mut Vec, filter_fn: &impl Fn(Res) -> bool, + ctxt: Option, ) { for (key, resolution) in self.resolutions(module).borrow().iter() { if let Some(binding) = resolution.borrow().binding { let res = binding.res(); - if filter_fn(res) { + if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == key.ident.span.ctxt()) { names.push(TypoSuggestion::typo_from_res(key.ident.name, res)); } } @@ -1181,10 +1184,10 @@ impl<'a> Resolver<'a> { Scope::CrateRoot => { let root_ident = Ident::new(kw::PathRoot, ident.span); let root_module = this.resolve_crate_root(root_ident); - this.add_module_candidates(root_module, &mut suggestions, filter_fn); + this.add_module_candidates(root_module, &mut suggestions, filter_fn, None); } Scope::Module(module, _) => { - this.add_module_candidates(module, &mut suggestions, filter_fn); + this.add_module_candidates(module, &mut suggestions, filter_fn, None); } Scope::MacroUsePrelude => { suggestions.extend(this.macro_use_prelude.iter().filter_map( @@ -1221,7 +1224,7 @@ impl<'a> Resolver<'a> { Scope::StdLibPrelude => { if let Some(prelude) = this.prelude { let mut tmp_suggestions = Vec::new(); - this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn); + this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); suggestions.extend( tmp_suggestions .into_iter() diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 13cd7987e92..3c82c760ed7 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -131,6 +131,7 @@ pub(super) enum LifetimeElisionCandidate { } /// Only used for diagnostics. +#[derive(Debug)] struct BaseError { msg: String, fallback_label: String, @@ -1584,19 +1585,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ) -> Option { let mut names = Vec::new(); if path.len() == 1 { + let mut ctxt = path.last().unwrap().ident.span.ctxt(); + // Search in lexical scope. // Walk backwards up the ribs in scope and collect candidates. for rib in self.ribs[ns].iter().rev() { + let rib_ctxt = if rib.kind.contains_params() { + ctxt.normalize_to_macros_2_0() + } else { + ctxt.normalize_to_macro_rules() + }; + // Locals and type parameters for (ident, &res) in &rib.bindings { - if filter_fn(res) { + if filter_fn(res) && ident.span.ctxt() == rib_ctxt { names.push(TypoSuggestion::typo_from_res(ident.name, res)); } } + + if let RibKind::MacroDefinition(def) = rib.kind && def == self.r.macro_def(ctxt) { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ctxt.remove_mark(); + continue; + } + // Items in scope if let RibKind::ModuleRibKind(module) = rib.kind { // Items from this module - self.r.add_module_candidates(module, &mut names, &filter_fn); + self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt)); if let ModuleKind::Block = module.kind { // We can see through blocks @@ -1622,7 +1639,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { })); if let Some(prelude) = self.r.prelude { - self.r.add_module_candidates(prelude, &mut names, &filter_fn); + self.r.add_module_candidates(prelude, &mut names, &filter_fn, None); } } break; @@ -1641,7 +1658,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, Some(TypeNS), None) { - self.r.add_module_candidates(module, &mut names, &filter_fn); + self.r.add_module_candidates(module, &mut names, &filter_fn, None); } } -- cgit 1.4.1-3-g733a5 From 4bbb163b5d248bef036670d952f1c00dd812a7e2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 16 Oct 2022 08:59:28 +0000 Subject: Point to shadowed name when it exists. --- compiler/rustc_resolve/src/late/diagnostics.rs | 45 +++++++++++++++++++--- src/test/ui/lexical-scopes.stderr | 2 + ...at-type-parameter-shadowing-another-type.stderr | 13 +++++-- 3 files changed, 51 insertions(+), 9 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3c82c760ed7..ffa7cb0c9d2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -141,6 +141,22 @@ struct BaseError { suggestion: Option<(Span, &'static str, String)>, } +#[derive(Debug)] +enum TypoCandidate { + Typo(TypoSuggestion), + Shadowed(Res), + None, +} + +impl TypoCandidate { + fn to_opt_suggestion(self) -> Option { + match self { + TypoCandidate::Typo(sugg) => Some(sugg), + TypoCandidate::Shadowed(_) | TypoCandidate::None => None, + } + } +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option { match def_id.krate { @@ -497,7 +513,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } // Try Levenshtein algorithm. - let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); + let typo_sugg = + self.lookup_typo_candidate(path, source.namespace(), is_expected).to_opt_suggestion(); if path.len() == 1 && self.self_type_is_available() { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { let self_is_available = self.self_value_is_available(path[0].ident.span); @@ -661,7 +678,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_expected = &|res| source.is_expected(res); let ident_span = path.last().map_or(span, |ident| ident.ident.span); let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); + if let TypoCandidate::Shadowed(res) = typo_sugg + && let Some(id) = res.opt_def_id() + && let Some(sugg_span) = self.r.opt_span(id) + { + err.span_label( + sugg_span, + format!("you might have meant to refer to this {}", res.descr()), + ); + return true; + } let mut fallback = false; + let typo_sugg = typo_sugg.to_opt_suggestion(); if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { fallback = true; match self.diagnostic_metadata.current_let_binding { @@ -1582,7 +1610,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { path: &[Segment], ns: Namespace, filter_fn: &impl Fn(Res) -> bool, - ) -> Option { + ) -> TypoCandidate { let mut names = Vec::new(); if path.len() == 1 { let mut ctxt = path.last().unwrap().ident.span.ctxt(); @@ -1671,10 +1699,17 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { name, None, ) { - Some(found) if found != name => { - names.into_iter().find(|suggestion| suggestion.candidate == found) + Some(found) => { + let Some(sugg) = names.into_iter().find(|suggestion| suggestion.candidate == found) else { + return TypoCandidate::None; + }; + if found == name { + TypoCandidate::Shadowed(sugg.res) + } else { + TypoCandidate::Typo(sugg) + } } - _ => None, + _ => TypoCandidate::None, } } diff --git a/src/test/ui/lexical-scopes.stderr b/src/test/ui/lexical-scopes.stderr index 08e4be2c0c3..53598545223 100644 --- a/src/test/ui/lexical-scopes.stderr +++ b/src/test/ui/lexical-scopes.stderr @@ -1,6 +1,8 @@ error[E0574]: expected struct, variant or union type, found type parameter `T` --> $DIR/lexical-scopes.rs:3:13 | +LL | struct T { i: i32 } + | ------------------- you might have meant to refer to this struct LL | fn f() { | - found this type parameter LL | let t = T { i: 0 }; diff --git a/src/test/ui/resolve/point-at-type-parameter-shadowing-another-type.stderr b/src/test/ui/resolve/point-at-type-parameter-shadowing-another-type.stderr index af9f4612ab3..eb26cd9cabb 100644 --- a/src/test/ui/resolve/point-at-type-parameter-shadowing-another-type.stderr +++ b/src/test/ui/resolve/point-at-type-parameter-shadowing-another-type.stderr @@ -1,11 +1,16 @@ error[E0574]: expected struct, variant or union type, found type parameter `Baz` --> $DIR/point-at-type-parameter-shadowing-another-type.rs:16:13 | -LL | impl Foo for Bar { - | --- found this type parameter +LL | / struct Baz { +LL | | num: usize, +LL | | } + | |_- you might have meant to refer to this struct +LL | +LL | impl Foo for Bar { + | --- found this type parameter ... -LL | Baz { num } => num, - | ^^^ not a struct, variant or union type +LL | Baz { num } => num, + | ^^^ not a struct, variant or union type error: aborting due to previous error -- cgit 1.4.1-3-g733a5