From bd8813e52c5120c40ec3cca096ff4a98fdf56be8 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 24 Jul 2019 01:42:39 +0200 Subject: [PATCH] Add method disambiguation help for trait implementation Closes #51046 Closes #40471 --- src/librustc_typeck/check/method/suggest.rs | 50 ++++++++++++------- .../method-ambig-two-traits-from-impls.rs | 16 ++++++ .../method-ambig-two-traits-from-impls.stderr | 22 ++++++++ .../method-ambig-two-traits-from-impls2.rs | 16 ++++++ ...method-ambig-two-traits-from-impls2.stderr | 22 ++++++++ 5 files changed, 107 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/methods/method-ambig-two-traits-from-impls.rs create mode 100644 src/test/ui/methods/method-ambig-two-traits-from-impls.stderr create mode 100644 src/test/ui/methods/method-ambig-two-traits-from-impls2.rs create mode 100644 src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 5febc694def0c..cd4c8a28dab28 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -10,7 +10,6 @@ use rustc::hir::{self, ExprKind, Node, QPath}; use rustc::hir::def::{Res, DefKind}; use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::hir::map as hir_map; -use rustc::hir::print; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::traits::Obligation; use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable}; @@ -78,6 +77,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + let print_disambiguation_help = | + err: &mut DiagnosticBuilder<'_>, + trait_name: String, + | { + err.help(&format!( + "to disambiguate the method call, write `{}::{}({}{})` instead", + trait_name, + item_name, + if rcvr_ty.is_region_ptr() && args.is_some() { + if rcvr_ty.is_mutable_pointer() { + "&mut " + } else { + "&" + } + } else { + "" + }, + args.map(|arg| arg + .iter() + .map(|arg| self.tcx.sess.source_map().span_to_snippet(arg.span) + .unwrap_or_else(|_| "...".to_owned())) + .collect::>() + .join(", ") + ).unwrap_or_else(|| "...".to_owned()) + )); + }; + let report_candidates = | span: Span, err: &mut DiagnosticBuilder<'_>, @@ -139,6 +165,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.note(¬e_str); } + if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { + print_disambiguation_help(err, self.tcx.def_path_str(trait_ref.def_id)); + } } CandidateSource::TraitSource(trait_did) => { let item = match self.associated_item( @@ -163,24 +192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the candidate is defined in the trait `{}`", self.tcx.def_path_str(trait_did)); } - err.help(&format!("to disambiguate the method call, write `{}::{}({}{})` \ - instead", - self.tcx.def_path_str(trait_did), - item_name, - if rcvr_ty.is_region_ptr() && args.is_some() { - if rcvr_ty.is_mutable_pointer() { - "&mut " - } else { - "&" - } - } else { - "" - }, - args.map(|arg| arg.iter() - .map(|arg| print::to_string(print::NO_ANN, - |s| s.print_expr(arg))) - .collect::>() - .join(", ")).unwrap_or_else(|| "...".to_owned()))); + print_disambiguation_help(err, self.tcx.def_path_str(trait_did)); } } } diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls.rs b/src/test/ui/methods/method-ambig-two-traits-from-impls.rs new file mode 100644 index 0000000000000..22bf840660536 --- /dev/null +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls.rs @@ -0,0 +1,16 @@ +trait A { fn foo(self); } +trait B { fn foo(self); } + +struct AB {} + +impl A for AB { + fn foo(self) {} +} + +impl B for AB { + fn foo(self) {} +} + +fn main() { + AB {}.foo(); //~ ERROR E0034 +} diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr new file mode 100644 index 0000000000000..0b3724e030fa4 --- /dev/null +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls.stderr @@ -0,0 +1,22 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/method-ambig-two-traits-from-impls.rs:15:11 + | +LL | AB {}.foo(); + | ^^^ multiple `foo` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `AB` + --> $DIR/method-ambig-two-traits-from-impls.rs:7:5 + | +LL | fn foo(self) {} + | ^^^^^^^^^^^^ + = help: to disambiguate the method call, write `A::foo(AB {})` instead +note: candidate #2 is defined in an impl of the trait `B` for the type `AB` + --> $DIR/method-ambig-two-traits-from-impls.rs:11:5 + | +LL | fn foo(self) {} + | ^^^^^^^^^^^^ + = help: to disambiguate the method call, write `B::foo(AB {})` instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls2.rs b/src/test/ui/methods/method-ambig-two-traits-from-impls2.rs new file mode 100644 index 0000000000000..0a96c1223da36 --- /dev/null +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls2.rs @@ -0,0 +1,16 @@ +trait A { fn foo(); } +trait B { fn foo(); } + +struct AB {} + +impl A for AB { + fn foo() {} +} + +impl B for AB { + fn foo() {} +} + +fn main() { + AB::foo(); //~ ERROR E0034 +} diff --git a/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr new file mode 100644 index 0000000000000..81c99b33c813e --- /dev/null +++ b/src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr @@ -0,0 +1,22 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/method-ambig-two-traits-from-impls2.rs:15:5 + | +LL | AB::foo(); + | ^^^^^^^ multiple `foo` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `AB` + --> $DIR/method-ambig-two-traits-from-impls2.rs:7:5 + | +LL | fn foo() {} + | ^^^^^^^^ + = help: to disambiguate the method call, write `A::foo(...)` instead +note: candidate #2 is defined in an impl of the trait `B` for the type `AB` + --> $DIR/method-ambig-two-traits-from-impls2.rs:11:5 + | +LL | fn foo() {} + | ^^^^^^^^ + = help: to disambiguate the method call, write `B::foo(...)` instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`.