about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-04-30 18:44:01 +0000
committerbors <bors@rust-lang.org>2022-04-30 18:44:01 +0000
commit7c4b47696907d64eff5621a64eb3c6e795a9ec77 (patch)
tree6741f99bbc2c7b939e2488b2f4e8302a3b9badbd
parent579d26876ddc5426be32cca1a026b6fd7fc5be24 (diff)
parenta7213832618f25690a65b5c270309df687d6b869 (diff)
downloadrust-7c4b47696907d64eff5621a64eb3c6e795a9ec77.tar.gz
rust-7c4b47696907d64eff5621a64eb3c6e795a9ec77.zip
Auto merge of #96347 - estebank:issue-96292, r=compiler-errors
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
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs41
-rw-r--r--src/test/ui/error-codes/E0283.stderr5
-rw-r--r--src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs20
-rw-r--r--src/test/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr37
-rw-r--r--src/test/ui/traits/issue-77982.stderr8
5 files changed, 96 insertions, 15 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 a9a92fdbd64..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
@@ -734,22 +734,28 @@ 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| {
-                        format!(
-                            "{}::{}({})",
-                            candidate, segment.ident, path_segment.ident
-                        )
-                    });
-                    err.span_suggestions(
-                        e.span,
+                    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)),
+                            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())
+                            },
+                        ]
+                    }).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,
                     );
                 }
@@ -1037,3 +1043,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/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: `<Impl as Into<u32>>::into(foo_impl)`
    |
 note: multiple `impl`s satisfying `Impl: Into<_>` found
   --> $DIR/E0283.rs:17:1
@@ -24,6 +23,10 @@ LL | impl Into<u32> for Impl {
    = note: and another `impl` found in the `core` crate:
            - impl<T, U> Into<U> for T
              where U: From<T>;
+help: use the fully qualified path for the potential candidate
+   |
+LL |     let bar = <Impl as Into<u32>>::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
new file mode 100644
index 00000000000..9a444be500c
--- /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>(X);
+
+trait Method<T> {
+    fn method(self, _: i32) -> T;
+}
+
+impl<X> Method<i32> for Thing<X> {
+    fn method(self, _: i32) -> i32 { 0 }
+}
+
+impl<X> Method<u32> for Thing<X> {
+    fn method(self, _: i32) -> u32 { 0 }
+}
+
+fn main() {
+    let thing = Thing(true);
+    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
new file mode 100644
index 00000000000..0e52420ec43
--- /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(42);
+   |     ------^^^^^^----
+   |     |     |
+   |     |     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(42);
+   |     ------^^^^^^----
+   |     |     |
+   |     |     cannot infer type for type parameter `T` declared on the trait `Method`
+   |     this method call resolves to `T`
+   |
+note: multiple `impl`s satisfying `Thing<bool>: Method<_>` found
+  --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:7:1
+   |
+LL | impl<X> Method<i32> for Thing<X> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl<X> Method<u32> for Thing<X> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: use the fully qualified path for the potential candidates
+   |
+LL |     <Thing<_> as Method<i32>>::method(thing, 42);
+   |     ++++++++++++++++++++++++++++++++++     ~
+LL |     <Thing<_> as Method<u32>>::method(thing, 42);
+   |     ++++++++++++++++++++++++++++++++++     ~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr
index 413225d45a6..63c1cb3791e 100644
--- a/src/test/ui/traits/issue-77982.stderr
+++ b/src/test/ui/traits/issue-77982.stderr
@@ -37,13 +37,13 @@ LL |     opts.get(opt.as_ref());
 help: use the fully qualified path for the potential candidates
    |
 LL |     opts.get(<String as AsRef<OsStr>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              +++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<Path>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              ++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<[u8]>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              ++++++++++++++++++++++++++++++++   ~
 LL |     opts.get(<String as AsRef<str>>::as_ref(opt));
-   |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |              +++++++++++++++++++++++++++++++   ~
      and 4 other candidates
 
 error[E0283]: type annotations needed