From acee1f47ef5d75bc9f9043f2be59e4e6ced6892b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 23 Apr 2022 11:22:51 -0700 Subject: [PATCH 1/3] Erase type params when suggesting fully qualified path When suggesting the use of a fully qualified path for a method call that is ambiguous because it has multiple candidates, erase type params in the resulting code, as they would result in an error when applied. We replace them with `_` in the output to rely on inference. There might be cases where this still produces slighlty incomplete suggestions, but it otherwise produces many more errors in relatively common cases. Fix #96292 --- .../infer/error_reporting/need_type_info.rs | 17 +++++++++ ...arams-by-name-in-suggestion-issue-96292.rs | 20 ++++++++++ ...s-by-name-in-suggestion-issue-96292.stderr | 37 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs create mode 100644 src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index a9a92fdbd64..c851d12b3e6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -736,8 +736,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind && let [path_segment] = path.segments { + let mut eraser = TypeParamEraser(self.tcx); let candidate_len = impl_candidates.len(); let suggestions = impl_candidates.iter().map(|candidate| { + let candidate = candidate.super_fold_with(&mut eraser); format!( "{}::{}({})", candidate, segment.ident, path_segment.ident @@ -1037,3 +1039,18 @@ impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> { } } } + +/// Replace type parameters with `ty::Infer(ty::Var)` to display `_`. +struct TypeParamEraser<'tcx>(TyCtxt<'tcx>); + +impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.0 + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Param(_) | ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)), + _ => t.super_fold_with(self), + } + } +} diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs new file mode 100644 index 00000000000..f08025d99b5 --- /dev/null +++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs @@ -0,0 +1,20 @@ +struct Thing(X); + +trait Method { + fn method(self) -> T; +} + +impl Method for Thing { + fn method(self) -> i32 { 0 } +} + +impl Method for Thing { + fn method(self) -> u32 { 0 } +} + +fn main() { + let thing = Thing(true); + thing.method(); + //~^ ERROR type annotations needed + //~| ERROR type annotations needed +} diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr new file mode 100644 index 00000000000..2e80fa89f63 --- /dev/null +++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr @@ -0,0 +1,37 @@ +error[E0282]: type annotations needed + --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 + | +LL | thing.method(); + | ------^^^^^^-- + | | | + | | cannot infer type for type parameter `T` declared on the trait `Method` + | this method call resolves to `T` + +error[E0283]: type annotations needed + --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 + | +LL | thing.method(); + | ------^^^^^^-- + | | | + | | cannot infer type for type parameter `T` declared on the trait `Method` + | this method call resolves to `T` + | +note: multiple `impl`s satisfying `Thing: Method<_>` found + --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:7:1 + | +LL | impl Method for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Method for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the fully qualified path for the potential candidates + | +LL | as Method>::method(thing); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | as Method>::method(thing); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. From 1e1f33f584af064a42e782a819fa3bd6ff4bcd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 23 Apr 2022 12:44:52 -0700 Subject: [PATCH 2/3] Use more targetted suggestion span for fully qualified path --- .../src/infer/error_reporting/need_type_info.rs | 17 ++++++++++------- src/test/ui/error-codes/E0283.stderr | 5 ++++- ...-params-by-name-in-suggestion-issue-96292.rs | 8 ++++---- ...ams-by-name-in-suggestion-issue-96292.stderr | 16 ++++++++-------- src/test/ui/traits/issue-77982.stderr | 16 ++++++++-------- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index c851d12b3e6..623d655bcbb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -734,19 +734,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if !impl_candidates.is_empty() && e.span.contains(span) && let Some(expr) = exprs.first() && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind - && let [path_segment] = path.segments + && let [_] = path.segments { let mut eraser = TypeParamEraser(self.tcx); let candidate_len = impl_candidates.len(); let suggestions = impl_candidates.iter().map(|candidate| { let candidate = candidate.super_fold_with(&mut eraser); - format!( - "{}::{}({})", - candidate, segment.ident, path_segment.ident - ) + vec![ + (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)), + if exprs.len() == 1 { + (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string()) + } else { + (expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string()) + }, + ] }); - err.span_suggestions( - e.span, + err.multipart_suggestions( &format!( "use the fully qualified path for the potential candidate{}", pluralize!(candidate_len), diff --git a/src/test/ui/error-codes/E0283.stderr b/src/test/ui/error-codes/E0283.stderr index 7dcfe96b35c..e2bab486064 100644 --- a/src/test/ui/error-codes/E0283.stderr +++ b/src/test/ui/error-codes/E0283.stderr @@ -14,7 +14,6 @@ LL | let bar = foo_impl.into() * 1u32; | | | | | cannot infer type for type parameter `T` declared on the trait `Into` | this method call resolves to `T` - | help: use the fully qualified path for the potential candidate: `>::into(foo_impl)` | note: multiple `impl`s satisfying `Impl: Into<_>` found --> $DIR/E0283.rs:17:1 @@ -24,6 +23,10 @@ LL | impl Into for Impl { = note: and another `impl` found in the `core` crate: - impl Into for T where U: From; +help: use the fully qualified path for the potential candidate + | +LL | let bar = >::into(foo_impl) * 1u32; + | ++++++++++++++++++++++++++ ~ error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs index f08025d99b5..9a444be500c 100644 --- a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs +++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs @@ -1,20 +1,20 @@ struct Thing(X); trait Method { - fn method(self) -> T; + fn method(self, _: i32) -> T; } impl Method for Thing { - fn method(self) -> i32 { 0 } + fn method(self, _: i32) -> i32 { 0 } } impl Method for Thing { - fn method(self) -> u32 { 0 } + fn method(self, _: i32) -> u32 { 0 } } fn main() { let thing = Thing(true); - thing.method(); + thing.method(42); //~^ ERROR type annotations needed //~| ERROR type annotations needed } diff --git a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr index 2e80fa89f63..0e52420ec43 100644 --- a/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr +++ b/src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 | -LL | thing.method(); - | ------^^^^^^-- +LL | thing.method(42); + | ------^^^^^^---- | | | | | cannot infer type for type parameter `T` declared on the trait `Method` | this method call resolves to `T` @@ -10,8 +10,8 @@ LL | thing.method(); error[E0283]: type annotations needed --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 | -LL | thing.method(); - | ------^^^^^^-- +LL | thing.method(42); + | ------^^^^^^---- | | | | | cannot infer type for type parameter `T` declared on the trait `Method` | this method call resolves to `T` @@ -26,10 +26,10 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the fully qualified path for the potential candidates | -LL | as Method>::method(thing); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | as Method>::method(thing); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | as Method>::method(thing, 42); + | ++++++++++++++++++++++++++++++++++ ~ +LL | as Method>::method(thing, 42); + | ++++++++++++++++++++++++++++++++++ ~ error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr index 413225d45a6..414e9e580c1 100644 --- a/src/test/ui/traits/issue-77982.stderr +++ b/src/test/ui/traits/issue-77982.stderr @@ -36,14 +36,14 @@ LL | opts.get(opt.as_ref()); - impl AsRef for String; help: use the fully qualified path for the potential candidates | -LL | opts.get(>::as_ref(opt)); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | opts.get(>::as_ref(opt)); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | opts.get(>::as_ref(opt)); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | opts.get(>::as_ref(opt)); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | opts.get(>::as_ref(opt)); + | +++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | ++++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | ++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | +++++++++++++++++++++++++++++ ~ and 4 other candidates error[E0283]: type annotations needed From a7213832618f25690a65b5c270309df687d6b869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 23 Apr 2022 17:54:11 -0700 Subject: [PATCH 3/3] Provide consistent output order for suggestions --- .../src/infer/error_reporting/need_type_info.rs | 7 ++++--- src/test/ui/traits/issue-77982.stderr | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 623d655bcbb..465358de932 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -738,7 +738,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { let mut eraser = TypeParamEraser(self.tcx); let candidate_len = impl_candidates.len(); - let suggestions = impl_candidates.iter().map(|candidate| { + let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| { let candidate = candidate.super_fold_with(&mut eraser); vec![ (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)), @@ -748,13 +748,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string()) }, ] - }); + }).collect(); + suggestions.sort_by(|a, b| a[0].1.cmp(&b[0].1)); err.multipart_suggestions( &format!( "use the fully qualified path for the potential candidate{}", pluralize!(candidate_len), ), - suggestions, + suggestions.into_iter(), Applicability::MaybeIncorrect, ); } diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr index 414e9e580c1..63c1cb3791e 100644 --- a/src/test/ui/traits/issue-77982.stderr +++ b/src/test/ui/traits/issue-77982.stderr @@ -36,14 +36,14 @@ LL | opts.get(opt.as_ref()); - impl AsRef for String; help: use the fully qualified path for the potential candidates | -LL | opts.get(>::as_ref(opt)); - | +++++++++++++++++++++++++++++ ~ -LL | opts.get(>::as_ref(opt)); - | ++++++++++++++++++++++++++++++ ~ -LL | opts.get(>::as_ref(opt)); - | ++++++++++++++++++++++++++++ ~ -LL | opts.get(>::as_ref(opt)); - | +++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | +++++++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | ++++++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | ++++++++++++++++++++++++++++++++ ~ +LL | opts.get(>::as_ref(opt)); + | +++++++++++++++++++++++++++++++ ~ and 4 other candidates error[E0283]: type annotations needed