diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index e3265431adda5..40e43c72e0f19 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3304,7 +3304,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 { @@ -3332,9 +3332,53 @@ 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 = + |hir_map: rustc_middle::hir::map::Map<'_>, + local_def_id: Option| + -> bool { + match local_def_id { + Some(local_def_id) => match hir_map.find_by_def_id(local_def_id) { + Some(node) => match node { + Node::ImplItem(it) => match it.kind { + hir::ImplItemKind::Fn(_, body_id) => { + let body = hir_map.body(body_id); + match body.params.get(0) { + Some(first_param) => { + if let rustc_hir::PatKind::Binding(_, _, ident, _) = + &first_param.pat.kind + { + ident.name.as_str() == "self" + } else { + false + } + } + None => false, + } + } + _ => false, + }, + _ => false, + }, + None => false, + }, + None => 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(tcx.hir(), 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