review comments: clean up

This commit is contained in:
Esteban Küber 2024-02-14 18:11:19 +00:00
parent f566867ace
commit b59bd7fd16
2 changed files with 166 additions and 160 deletions

View File

@ -563,80 +563,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let suggest_confusable = |err: &mut Diagnostic| {
if let Some(call_name) = call_ident
&& let Some(callee_ty) = callee_ty
{
let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect();
// Check for other methods in the following order
// - methods marked as `rustc_confusables` with the provided arguments
// - methods with the same argument type/count and short levenshtein distance
// - methods marked as `rustc_confusables` (done)
// - methods with short levenshtein distance
let call_name = call_ident?;
let callee_ty = callee_ty?;
let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect();
// Check for other methods in the following order
// - methods marked as `rustc_confusables` with the provided arguments
// - methods with the same argument type/count and short levenshtein distance
// - methods marked as `rustc_confusables` (done)
// - methods with short levenshtein distance
// Look for commonly confusable method names considering arguments.
self.confusable_method_name(
err,
callee_ty.peel_refs(),
call_name,
Some(input_types.clone()),
)
.or_else(|| {
// Look for method names with short levenshtein distance, considering arguments.
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
&& fn_sig.inputs()[1..]
.iter()
.zip(input_types.iter())
.all(|(expected, found)| self.can_coerce(*expected, *found))
&& fn_sig.inputs()[1..].len() == input_types.len()
{
err.span_suggestion_verbose(
call_name.span,
format!("you might have meant to use `{}`", assoc.name),
assoc.name,
Applicability::MaybeIncorrect,
);
return Some(assoc.name);
}
None
})
.or_else(|| {
// Look for commonly confusable method names disregarding arguments.
self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None)
})
.or_else(|| {
// Look for similarly named methods with levenshtein distance with the right
// number of arguments.
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
&& fn_sig.inputs()[1..].len() == input_types.len()
{
err.span_note(
tcx.def_span(assoc.def_id),
format!(
"there's is a method with similar name `{}`, but the arguments \
don't match",
assoc.name,
),
);
return Some(assoc.name);
}
None
})
.or_else(|| {
// Fallthrough: look for similarly named methods with levenshtein distance.
if let Some((assoc, _)) = similar_assoc(call_name) {
err.span_note(
tcx.def_span(assoc.def_id),
format!(
"there's is a method with similar name `{}`, but their argument \
count doesn't match",
assoc.name,
),
);
return Some(assoc.name);
}
None
});
// Look for commonly confusable method names considering arguments.
if let Some(name) = self.confusable_method_name(
err,
callee_ty.peel_refs(),
call_name,
Some(input_types.clone()),
) {
return Some(name);
}
// Look for method names with short levenshtein distance, considering arguments.
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
&& fn_sig.inputs()[1..]
.iter()
.zip(input_types.iter())
.all(|(expected, found)| self.can_coerce(*expected, *found))
&& fn_sig.inputs()[1..].len() == input_types.len()
{
err.span_suggestion_verbose(
call_name.span,
format!("you might have meant to use `{}`", assoc.name),
assoc.name,
Applicability::MaybeIncorrect,
);
return Some(assoc.name);
}
// Look for commonly confusable method names disregarding arguments.
if let Some(name) =
self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None)
{
return Some(name);
}
// Look for similarly named methods with levenshtein distance with the right
// number of arguments.
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
&& fn_sig.inputs()[1..].len() == input_types.len()
{
err.span_note(
tcx.def_span(assoc.def_id),
format!(
"there's is a method with similar name `{}`, but the arguments don't match",
assoc.name,
),
);
return Some(assoc.name);
}
// Fallthrough: look for similarly named methods with levenshtein distance.
if let Some((assoc, _)) = similar_assoc(call_name) {
err.span_note(
tcx.def_span(assoc.def_id),
format!(
"there's is a method with similar name `{}`, but their argument count \
doesn't match",
assoc.name,
),
);
return Some(assoc.name);
}
None
};
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
// and treats error types differently

View File

@ -1358,94 +1358,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// ...or if we already suggested that name because of `rustc_confusable` annotation.
&& Some(similar_candidate.name) != confusable_suggested
{
let def_kind = similar_candidate.kind.as_def_kind();
let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id);
// Methods are defined within the context of a struct and their first parameter
// is always `self`, which represents the instance of the struct the method is
// being called on Associated functions dont take self as a parameter and they are
// not methods because they dont have an instance of the struct to work with.
if def_kind == DefKind::AssocFn {
let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id);
let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args);
let fn_sig =
self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig);
if similar_candidate.fn_has_self_parameter {
if let Some(args) = args
&& fn_sig.inputs()[1..].len() == args.len()
{
// We found a method with the same number of arguments as the method
// call expression the user wrote.
err.span_suggestion_verbose(
span,
format!("there is {an} method with a similar name"),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
// We found a method but either the expression is not a method call or
// the argument count didn't match.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} method `{}` with a similar name{}",
similar_candidate.name,
if let None = args {
""
} else {
", but with different arguments"
},
),
);
}
} else if let Some(args) = args
&& fn_sig.inputs().len() == args.len()
{
// We have fn call expression and the argument count match the associated
// function we found.
err.span_suggestion_verbose(
span,
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
);
}
} else if let Mode::Path = mode
&& args.unwrap_or(&[]).is_empty()
{
// We have an associated item syntax and we found something that isn't an fn.
err.span_suggestion_verbose(
span,
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
// The expression is a function or method call, but the item we found is an
// associated const or type.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
);
}
self.find_likely_intended_associated_item(
&mut err,
similar_candidate,
span,
args,
mode,
);
}
}
// If an appropriate error source is not found, check method chain for possible candiates
@ -1497,6 +1416,100 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(err)
}
fn find_likely_intended_associated_item(
&self,
err: &mut Diagnostic,
similar_candidate: ty::AssocItem,
span: Span,
args: Option<&'tcx [hir::Expr<'tcx>]>,
mode: Mode,
) {
let tcx = self.tcx;
let def_kind = similar_candidate.kind.as_def_kind();
let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id);
// Methods are defined within the context of a struct and their first parameter
// is always `self`, which represents the instance of the struct the method is
// being called on Associated functions dont take self as a parameter and they are
// not methods because they dont have an instance of the struct to work with.
if def_kind == DefKind::AssocFn {
let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id);
let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args);
let fn_sig = self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig);
if similar_candidate.fn_has_self_parameter {
if let Some(args) = args
&& fn_sig.inputs()[1..].len() == args.len()
{
// We found a method with the same number of arguments as the method
// call expression the user wrote.
err.span_suggestion_verbose(
span,
format!("there is {an} method with a similar name"),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
// We found a method but either the expression is not a method call or
// the argument count didn't match.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} method `{}` with a similar name{}",
similar_candidate.name,
if let None = args { "" } else { ", but with different arguments" },
),
);
}
} else if let Some(args) = args
&& fn_sig.inputs().len() == args.len()
{
// We have fn call expression and the argument count match the associated
// function we found.
err.span_suggestion_verbose(
span,
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
);
}
} else if let Mode::Path = mode
&& args.unwrap_or(&[]).is_empty()
{
// We have an associated item syntax and we found something that isn't an fn.
err.span_suggestion_verbose(
span,
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
// The expression is a function or method call, but the item we found is an
// associated const or type.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
);
}
}
pub(crate) fn confusable_method_name(
&self,
err: &mut Diagnostic,