diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6530d828b3be2..47fdd64796e9d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3306,6 +3306,7 @@ fn print_disambiguation_help<'tcx>( span: Span, item: ty::AssocItem, ) -> Option { + 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 { @@ -3318,27 +3319,34 @@ fn print_disambiguation_help<'tcx>( { let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id); let item_name = item.ident(tcx); - let rcvr_ref = tcx - .fn_sig(item.def_id) - .skip_binder() - .skip_binder() - .inputs() - .get(0) - .and_then(|ty| ty.ref_mutability()) - .map_or("", |mutbl| mutbl.ref_prefix_str()); - let args = format!( - "({}{})", - rcvr_ref, - std::iter::once(receiver) - .chain(args.iter()) - .map(|arg| tcx - .sess - .source_map() - .span_to_snippet(arg.span) - .unwrap_or_else(|_| { "_".to_owned() })) - .collect::>() - .join(", "), + let first_input = + tcx.fn_sig(item.def_id).instantiate_identity().skip_binder().inputs().get(0); + let (first_arg_type, rcvr_ref) = ( + first_input.map(|first| first.peel_refs()), + first_input + .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 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 + || item.fn_has_self_parameter) + { + Some(receiver) + } else { + None + } + .into_iter() + .chain(args) + .map(|arg| { + tcx.sess.source_map().span_to_snippet(arg.span).unwrap_or_else(|_| "_".to_owned()) + }) + .collect::>() + .join(", "); + + let args = format!("({}{})", rcvr_ref, args); err.span_suggestion_verbose( span, format!( diff --git a/tests/ui/methods/disambiguate-associated-function-first-arg.rs b/tests/ui/methods/disambiguate-associated-function-first-arg.rs new file mode 100644 index 0000000000000..4c8192fc14bd3 --- /dev/null +++ b/tests/ui/methods/disambiguate-associated-function-first-arg.rs @@ -0,0 +1,49 @@ +struct A {} + +fn main() { + let _a = A {}; + _a.new(1); + //~^ ERROR no method named `new` found for struct `A` in the current scope +} + +trait M { + fn new(_a: i32); +} +impl M for A { + fn new(_a: i32) {} +} + +trait N { + fn new(_a: Self, _b: i32); +} +impl N for A { + fn new(_a: Self, _b: i32) {} +} + +trait O { + fn new(_a: Self, _b: i32); +} +impl O for A { + fn new(_a: A, _b: i32) {} +} + +struct S; + +trait TraitA { + fn f(self); +} +trait TraitB { + fn f(self); +} + +impl TraitA for T { + fn f(self) {} +} +impl TraitB for T { + fn f(self) {} +} + +fn test() { + S.f(); + //~^ multiple applicable items in scope +} diff --git a/tests/ui/methods/disambiguate-associated-function-first-arg.stderr b/tests/ui/methods/disambiguate-associated-function-first-arg.stderr new file mode 100644 index 0000000000000..341b7a9100329 --- /dev/null +++ b/tests/ui/methods/disambiguate-associated-function-first-arg.stderr @@ -0,0 +1,67 @@ +error[E0599]: no method named `new` found for struct `A` in the current scope + --> $DIR/disambiguate-associated-function-first-arg.rs:5:8 + | +LL | struct A {} + | -------- method `new` not found for this struct +... +LL | _a.new(1); + | ^^^ this is an associated function, not a method + | + = note: found the following associated functions; to be used as methods, functions must have a `self` parameter +note: candidate #1 is defined in the trait `M` + --> $DIR/disambiguate-associated-function-first-arg.rs:10:5 + | +LL | fn new(_a: i32); + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `N` + --> $DIR/disambiguate-associated-function-first-arg.rs:17:5 + | +LL | fn new(_a: Self, _b: i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `O` + --> $DIR/disambiguate-associated-function-first-arg.rs:24:5 + | +LL | fn new(_a: Self, _b: i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the associated function for candidate #1 + | +LL | ::new(1); + | ~~~~~~~~~~~~~~~~ +help: disambiguate the associated function for candidate #2 + | +LL | ::new(_a, 1); + | ~~~~~~~~~~~~~~~~~~~~ +help: disambiguate the associated function for candidate #3 + | +LL | ::new(_a, 1); + | ~~~~~~~~~~~~~~~~~~~~ + +error[E0034]: multiple applicable items in scope + --> $DIR/disambiguate-associated-function-first-arg.rs:47:7 + | +LL | S.f(); + | ^ multiple `f` found + | +note: candidate #1 is defined in an impl of the trait `TraitA` for the type `T` + --> $DIR/disambiguate-associated-function-first-arg.rs:40:5 + | +LL | fn f(self) {} + | ^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `TraitB` for the type `T` + --> $DIR/disambiguate-associated-function-first-arg.rs:43:5 + | +LL | fn f(self) {} + | ^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL | TraitA::f(S); + | ~~~~~~~~~~~~ +help: disambiguate the method for candidate #2 + | +LL | TraitB::f(S); + | ~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0034, E0599. +For more information about an error, try `rustc --explain E0034`. 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