diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 503fb13664ae5..82181d3092051 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -33,7 +33,7 @@ use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; use rustc_middle::ty::IsSuggestable; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::def_id::DefIdSet; +use rustc_span::def_id::{self, DefIdSet}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span}; @@ -3312,7 +3312,7 @@ fn print_disambiguation_help<'tcx>( span: Span, item: ty::AssocItem, ) -> Option { - let trait_impl_type = trait_ref.self_ty(); + let trait_impl_type = trait_ref.self_ty().peel_refs(); let trait_ref = if item.fn_has_self_parameter { trait_ref.print_only_trait_name().to_string() } else { @@ -3340,9 +3340,43 @@ fn print_disambiguation_help<'tcx>( .get(0) .and_then(|ty| ty.ref_mutability()) .map_or("", |mutbl| mutbl.ref_prefix_str()); - // If the type of first arg of this assoc function is `Self` or current trait impl type, we need to take the receiver as args. Otherwise, we don't. - let args = if let Some(t) = first_arg_type - && (t.to_string() == "Self" || t == trait_impl_type) + + // this is for `arbitrary_self_types`(egde case), see `tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.rs` + // for the following example: + // + // impl NuisanceFoo for T { + // fn foo(self) {} + // } + // + // type of `self` is T(unspecified), which is not `Self` or current trait impl type, but the receiver still shouble be take as the first arg. + let arbitrary_self_types_check = |local_def_id: Option| -> bool { + local_def_id.and_then(|id| tcx.opt_hir_node_by_def_id(id)).map_or(false, |node| { + match node { + Node::ImplItem(it) => match it.kind { + hir::ImplItemKind::Fn(_, body_id) => { + let body = tcx.hir().body(body_id); + body.params.get(0).map_or(false, |first_param| { + match &first_param.pat.kind { + // check the name of first arg + rustc_hir::PatKind::Binding(_, _, ident, _) => { + ident.name.as_str() == "self" + } + _ => false, + } + }) + } + _ => false, + }, + _ => false, + } + }) + }; + + // If the type of first arg of this assoc function is `Self` or current trait impl type or `arbitrary_self_types`, we need to take the receiver as args. Otherwise, we don't. + let args = if let Some(first_arg_type) = first_arg_type + && (first_arg_type == tcx.types.self_param + || first_arg_type == trait_impl_type + || arbitrary_self_types_check(item.def_id.as_local())) { std::iter::once(receiver).chain(args.iter()).collect::>() } else { diff --git a/tests/ui/methods/method-ambiguity-no-rcvr.stderr b/tests/ui/methods/method-ambiguity-no-rcvr.stderr index 73f6043f256ea..3b6eb07393ac8 100644 --- a/tests/ui/methods/method-ambiguity-no-rcvr.stderr +++ b/tests/ui/methods/method-ambiguity-no-rcvr.stderr @@ -20,12 +20,12 @@ LL | fn foo() {} | ^^^^^^^^ help: disambiguate the associated function for candidate #1 | -LL | ::foo(Qux); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | ::foo(); + | ~~~~~~~~~~~~~~~~~~~ help: disambiguate the associated function for candidate #2 | -LL | ::foo(Qux); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | ::foo(); + | ~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error