Properly suggest E::assoc when we encounter E::Variant::assoc

Use the right span when encountering an enum variant followed by an associated item so we don't lose the associated item in the resulting code.

Do not suggest the thing twice, once as a removal of the associated item and a second time as a typo suggestion.
This commit is contained in:
Esteban Küber 2024-11-03 18:55:19 +00:00
parent 06fc531c96
commit e26ad8b67d
3 changed files with 38 additions and 16 deletions

View File

@ -459,7 +459,7 @@ pub(crate) fn smart_resolve_report_errors(
return (err, Vec::new()); 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, &mut err,
source, source,
path, 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); 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 { if fallback {
// Fallback label. // Fallback label.
@ -589,7 +597,16 @@ fn try_lookup_name_relaxed(
span: Span, span: Span,
res: Option<Res>, res: Option<Res>,
base_error: &BaseError, base_error: &BaseError,
) -> (bool, Vec<ImportSuggestion>) { ) -> (bool, FxHashSet<String>, Vec<ImportSuggestion>) {
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. // Try to lookup name in more relaxed fashion for better error reporting.
let ident = path.last().unwrap().ident; let ident = path.last().unwrap().ident;
let is_expected = &|res| source.is_expected(res); 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"); 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( err.span_suggestions(
span, span,
msg, msg,
@ -658,7 +680,8 @@ fn try_lookup_name_relaxed(
// Try finding a suitable replacement. // Try finding a suitable replacement.
let typo_sugg = self let typo_sugg = self
.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected) .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 if let [segment] = path
&& !matches!(source, PathSource::Delegation) && !matches!(source, PathSource::Delegation)
&& self.self_type_is_available() && self.self_type_is_available()
@ -719,7 +742,7 @@ fn try_lookup_name_relaxed(
} }
} }
self.r.add_typo_suggestion(err, typo_sugg, ident_span); 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. // 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})"), format!("self.{path_str}({args_snippet})"),
Applicability::MachineApplicable, 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. // 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); 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, ident.span,
format!("the binding `{path_str}` is available in a different scope in the same function"), 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); candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);
} }
(false, candidates) (false, suggested_candidates, candidates)
} }
fn suggest_trait_and_bounds( fn suggest_trait_and_bounds(
@ -869,13 +892,16 @@ fn suggest_typo(
following_seg: Option<&Segment>, following_seg: Option<&Segment>,
span: Span, span: Span,
base_error: &BaseError, base_error: &BaseError,
suggested_candidates: FxHashSet<String>,
) -> bool { ) -> bool {
let is_expected = &|res| source.is_expected(res); let is_expected = &|res| source.is_expected(res);
let ident_span = path.last().map_or(span, |ident| ident.ident.span); let ident_span = path.last().map_or(span, |ident| ident.ident.span);
let typo_sugg = let typo_sugg =
self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
let mut fallback = false; 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) { if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
fallback = true; fallback = true;
match self.diag_metadata.current_let_binding { match self.diag_metadata.current_let_binding {

View File

@ -6,7 +6,7 @@ LL | E::A::f();
| |
help: there is an enum variant `E::A`; try using the variant's enum 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 error: aborting due to 1 previous error

View File

@ -6,7 +6,7 @@ LL | E::V::associated_item;
| |
help: there is an enum variant `E::V`; try using the variant's enum 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 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 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; LL | E::associated_item;
| ~ | ~