diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f0632a21091..c80fbe1a1c7 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -459,7 +459,7 @@ pub(crate) fn smart_resolve_report_errors( return (err, Vec::new()); } - let (found, mut candidates) = self.try_lookup_name_relaxed( + let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed( &mut err, source, path, @@ -478,7 +478,15 @@ pub(crate) fn smart_resolve_report_errors( } let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); - fallback |= self.suggest_typo(&mut err, source, path, following_seg, span, &base_error); + fallback |= self.suggest_typo( + &mut err, + source, + path, + following_seg, + span, + &base_error, + suggested_candidates, + ); if fallback { // Fallback label. @@ -589,7 +597,16 @@ fn try_lookup_name_relaxed( span: Span, res: Option, base_error: &BaseError, - ) -> (bool, Vec) { + ) -> (bool, FxHashSet, Vec) { + let span = match following_seg { + Some(_) if path[0].ident.span.eq_ctxt(path[path.len() - 1].ident.span) => { + // The path `span` that comes in includes any following segments, which we don't + // want to replace in the suggestions. + path[0].ident.span.to(path[path.len() - 1].ident.span) + } + _ => span, + }; + let mut suggested_candidates = FxHashSet::default(); // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; let is_expected = &|res| source.is_expected(res); @@ -646,6 +663,11 @@ fn try_lookup_name_relaxed( }; let msg = format!("{preamble}try using the variant's enum"); + suggested_candidates.extend( + enum_candidates + .iter() + .map(|(_variant_path, enum_ty_path)| enum_ty_path.clone()), + ); err.span_suggestions( span, msg, @@ -658,7 +680,8 @@ fn try_lookup_name_relaxed( // Try finding a suitable replacement. let typo_sugg = self .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected) - .to_opt_suggestion(); + .to_opt_suggestion() + .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())); if let [segment] = path && !matches!(source, PathSource::Delegation) && self.self_type_is_available() @@ -719,7 +742,7 @@ fn try_lookup_name_relaxed( } } self.r.add_typo_suggestion(err, typo_sugg, ident_span); - return (true, candidates); + return (true, suggested_candidates, candidates); } // If the first argument in call is `self` suggest calling a method. @@ -737,7 +760,7 @@ fn try_lookup_name_relaxed( format!("self.{path_str}({args_snippet})"), Applicability::MachineApplicable, ); - return (true, candidates); + return (true, suggested_candidates, candidates); } } @@ -754,7 +777,7 @@ fn try_lookup_name_relaxed( ) { // We do this to avoid losing a secondary span when we override the main error span. self.r.add_typo_suggestion(err, typo_sugg, ident_span); - return (true, candidates); + return (true, suggested_candidates, candidates); } } @@ -772,7 +795,7 @@ fn try_lookup_name_relaxed( ident.span, format!("the binding `{path_str}` is available in a different scope in the same function"), ); - return (true, candidates); + return (true, suggested_candidates, candidates); } } } @@ -781,7 +804,7 @@ fn try_lookup_name_relaxed( candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg); } - (false, candidates) + (false, suggested_candidates, candidates) } fn suggest_trait_and_bounds( @@ -869,13 +892,16 @@ fn suggest_typo( following_seg: Option<&Segment>, span: Span, base_error: &BaseError, + suggested_candidates: FxHashSet, ) -> bool { 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, following_seg, source.namespace(), is_expected); let mut fallback = false; - let typo_sugg = typo_sugg.to_opt_suggestion(); + let typo_sugg = typo_sugg + .to_opt_suggestion() + .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())); if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { fallback = true; match self.diag_metadata.current_let_binding { diff --git a/tests/ui/enum/assoc-fn-call-on-variant.stderr b/tests/ui/enum/assoc-fn-call-on-variant.stderr index 4775201d2bb..47fc630c923 100644 --- a/tests/ui/enum/assoc-fn-call-on-variant.stderr +++ b/tests/ui/enum/assoc-fn-call-on-variant.stderr @@ -6,7 +6,7 @@ LL | E::A::f(); | help: there is an enum variant `E::A`; try using the variant's enum | -LL | E(); +LL | E::f(); | ~ error: aborting due to 1 previous error diff --git a/tests/ui/resolve/resolve-variant-assoc-item.stderr b/tests/ui/resolve/resolve-variant-assoc-item.stderr index ed157197d17..9a5a605ac05 100644 --- a/tests/ui/resolve/resolve-variant-assoc-item.stderr +++ b/tests/ui/resolve/resolve-variant-assoc-item.stderr @@ -6,7 +6,7 @@ LL | E::V::associated_item; | help: there is an enum variant `E::V`; try using the variant's enum | -LL | E; +LL | E::associated_item; | ~ error[E0433]: failed to resolve: `V` is a variant, not a module @@ -17,10 +17,6 @@ LL | V::associated_item; | help: there is an enum variant `E::V`; try using the variant's enum | -LL | E; - | ~ -help: an enum with a similar name exists - | LL | E::associated_item; | ~