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 associated method on deref types when path syntax method fails #100302

Merged
merged 1 commit into from
Sep 4, 2022
Merged
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
62 changes: 60 additions & 2 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_middle::traits::util::supertraits;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::ToPolyTraitRef;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
Expand All @@ -30,7 +30,7 @@ use rustc_trait_selection::traits::{
use std::cmp::Ordering;
use std::iter;

use super::probe::{Mode, ProbeScope};
use super::probe::{IsSuggestion, Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -1069,6 +1069,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);

return Some(err);
}

Expand Down Expand Up @@ -1651,6 +1653,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn check_for_deref_method(
&self,
err: &mut Diagnostic,
self_source: SelfSource<'tcx>,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
) {
let SelfSource::QPath(ty) = self_source else { return; };
for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
if let Ok(pick) = self.probe_for_name(
ty.span,
Mode::Path,
item_name,
IsSuggestion(true),
deref_ty,
ty.hir_id,
ProbeScope::TraitsInScope,
) {
if deref_ty.is_suggestable(self.tcx, true)
// If this method receives `&self`, then the provided
// argument _should_ coerce, so it's valid to suggest
// just changing the path.
&& pick.item.fn_has_self_parameter
&& let Some(self_ty) =
self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0)
&& self_ty.is_ref()
Comment on lines +1678 to +1681
Copy link
Member Author

@compiler-errors compiler-errors Aug 9, 2022

Choose a reason for hiding this comment

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

This is a bit of a strange heuristic, but it works pretty well. Part of the issue here is that we don't have type information for the self arg (yet), since we haven't called check_expr.

This is a MaybeIncorrect suggestion, so I think it's fine for it to be a bit incorrect, as long as it's not suggesting something wildly wrong.

{
let suggested_path = match deref_ty.kind() {
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Adt(_, _)
| ty::Str
| ty::Projection(_)
| ty::Param(_) => format!("{deref_ty}"),
_ => format!("<{deref_ty}>"),
};
err.span_suggestion_verbose(
ty.span,
format!("the function `{item_name}` is implemented on `{deref_ty}`"),
suggested_path,
Applicability::MaybeIncorrect,
);
} else {
err.span_note(
ty.span,
format!("the function `{item_name}` is implemented on `{deref_ty}`"),
);
}
return;
}
}
}

/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/suggestions/deref-path-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
let vec = Vec::new();
Vec::contains(&vec, &0);
//~^ ERROR no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope
//~| HELP the function `contains` is implemented on `[_]`
}
14 changes: 14 additions & 0 deletions src/test/ui/suggestions/deref-path-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0599]: no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope
--> $DIR/deref-path-method.rs:3:10
|
LL | Vec::contains(&vec, &0);
| ^^^^^^^^ function or associated item not found in `Vec<_, _>`
|
help: the function `contains` is implemented on `[_]`
|
LL | <[_]>::contains(&vec, &0);
| ~~~~~

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.