about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2021-04-08 13:50:47 -0700
committerEsteban Küber <esteban@kuber.com.ar>2021-04-08 13:50:47 -0700
commit147649d4b91b43c77f3f46c04b6daaa37f4e0955 (patch)
tree84c68d5dce9babbbc0ffb1ea92e3d131fc71eed7
parentd43ede10e4cc69547d1c05831ca43c9902304573 (diff)
downloadrust-147649d4b91b43c77f3f46c04b6daaa37f4e0955.tar.gz
rust-147649d4b91b43c77f3f46c04b6daaa37f4e0955.zip
Suggest changing impl parameter types to match trait
This is particularly useful for cases where arbitrary self types are
used, like in custom `Future`s.
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs50
-rw-r--r--src/test/ui/compare-method/bad-self-type.rs23
-rw-r--r--src/test/ui/compare-method/bad-self-type.stderr30
-rw-r--r--src/test/ui/compare-method/reordered-type-param.stderr6
-rw-r--r--src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr6
-rw-r--r--src/test/ui/impl-trait/trait_type.stderr5
-rw-r--r--src/test/ui/issues/issue-13033.stderr2
-rw-r--r--src/test/ui/issues/issue-20225.stderr15
-rw-r--r--src/test/ui/issues/issue-35869.stderr15
-rw-r--r--src/test/ui/mismatched_types/E0053.stderr7
-rw-r--r--src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr7
-rw-r--r--src/test/ui/wrong-mul-method-signature.stderr10
12 files changed, 151 insertions, 25 deletions
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 3a337dadc88..964aa8426ea 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -290,18 +290,55 @@ fn compare_predicate_entailment<'tcx>(
                 "method `{}` has an incompatible type for trait",
                 trait_m.ident
             );
-            if let TypeError::ArgumentMutability(_) = terr {
-                if let Some(trait_err_span) = trait_err_span {
-                    if let Ok(trait_err_str) = tcx.sess.source_map().span_to_snippet(trait_err_span)
+            match &terr {
+                TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
+                    if trait_m.fn_has_self_parameter =>
+                {
+                    let ty = trait_sig.inputs()[0];
+                    let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty())
                     {
+                        ExplicitSelf::ByValue => "self".to_owned(),
+                        ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+                        ExplicitSelf::ByReference(_, hir::Mutability::Mut) => {
+                            "&mut self".to_owned()
+                        }
+                        _ => format!("self: {}", ty),
+                    };
+
+                    // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
+                    // span points only at the type `Box<Self`>, but we want to cover the whole
+                    // argument pattern and type.
+                    let impl_m_hir_id =
+                        tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+                    let span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind {
+                        ImplItemKind::Fn(ref sig, body) => tcx
+                            .hir()
+                            .body_param_names(body)
+                            .zip(sig.decl.inputs.iter())
+                            .map(|(param, ty)| param.span.to(ty.span))
+                            .next()
+                            .unwrap_or(impl_err_span),
+                        _ => bug!("{:?} is not a method", impl_m),
+                    };
+
+                    diag.span_suggestion(
+                        span,
+                        "change the self-receiver type to match the trait",
+                        sugg,
+                        Applicability::MachineApplicable,
+                    );
+                }
+                TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
+                    if let Some(trait_ty) = trait_sig.inputs().get(*i) {
                         diag.span_suggestion(
                             impl_err_span,
-                            "consider changing the mutability to match the trait",
-                            trait_err_str,
+                            "change the parameter type to match the trait",
+                            trait_ty.to_string(),
                             Applicability::MachineApplicable,
                         );
                     }
                 }
+                _ => {}
             }
 
             infcx.note_type_err(
@@ -482,8 +519,7 @@ fn compare_self_type<'tcx>(
                 tcx.sess,
                 impl_m_span,
                 E0186,
-                "method `{}` has a `{}` declaration in the trait, but \
-                                            not in the impl",
+                "method `{}` has a `{}` declaration in the trait, but not in the impl",
                 trait_m.ident,
                 self_descr
             );
diff --git a/src/test/ui/compare-method/bad-self-type.rs b/src/test/ui/compare-method/bad-self-type.rs
new file mode 100644
index 00000000000..9eb978664bf
--- /dev/null
+++ b/src/test/ui/compare-method/bad-self-type.rs
@@ -0,0 +1,23 @@
+use std::future::Future;
+use std::task::{Context, Poll};
+
+fn main() {}
+
+struct MyFuture {}
+
+impl Future for MyFuture {
+    type Output = ();
+    fn poll(self, _: &mut Context<'_>) -> Poll<()> {
+    //~^ ERROR method `poll` has an incompatible type for trait
+        todo!()
+    }
+}
+
+trait T {
+    fn foo(self);
+}
+
+impl T for MyFuture {
+    fn foo(self: Box<Self>) {}
+    //~^ ERROR method `foo` has an incompatible type for trait
+}
diff --git a/src/test/ui/compare-method/bad-self-type.stderr b/src/test/ui/compare-method/bad-self-type.stderr
new file mode 100644
index 00000000000..4d85ff86df5
--- /dev/null
+++ b/src/test/ui/compare-method/bad-self-type.stderr
@@ -0,0 +1,30 @@
+error[E0053]: method `poll` has an incompatible type for trait
+  --> $DIR/bad-self-type.rs:10:13
+   |
+LL |     fn poll(self, _: &mut Context<'_>) -> Poll<()> {
+   |             ^^^^
+   |             |
+   |             expected struct `Pin`, found struct `MyFuture`
+   |             help: change the self-receiver type to match the trait: `self: Pin<&mut MyFuture>`
+   |
+   = note: expected fn pointer `fn(Pin<&mut MyFuture>, &mut Context<'_>) -> Poll<_>`
+              found fn pointer `fn(MyFuture, &mut Context<'_>) -> Poll<_>`
+
+error[E0053]: method `foo` has an incompatible type for trait
+  --> $DIR/bad-self-type.rs:21:18
+   |
+LL |     fn foo(self);
+   |            ---- type in trait
+...
+LL |     fn foo(self: Box<Self>) {}
+   |            ------^^^^^^^^^
+   |            |     |
+   |            |     expected struct `MyFuture`, found struct `Box`
+   |            help: change the self-receiver type to match the trait: `self`
+   |
+   = note: expected fn pointer `fn(MyFuture)`
+              found fn pointer `fn(Box<MyFuture>)`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0053`.
diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr
index f1f8a663f21..d581628ea48 100644
--- a/src/test/ui/compare-method/reordered-type-param.stderr
+++ b/src/test/ui/compare-method/reordered-type-param.stderr
@@ -5,8 +5,10 @@ LL |   fn b<C:Clone,D>(&self, x: C) -> C;
    |                             - type in trait
 ...
 LL |   fn b<F:Clone,G>(&self, _x: G) -> G { panic!() }
-   |        -       -             ^ expected type parameter `F`, found type parameter `G`
-   |        |       |
+   |        -       -             ^
+   |        |       |             |
+   |        |       |             expected type parameter `F`, found type parameter `G`
+   |        |       |             help: change the parameter type to match the trait: `F`
    |        |       found type parameter
    |        expected type parameter
    |
diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
index 638a0093fb2..d37670db085 100644
--- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
+++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
@@ -5,8 +5,10 @@ LL |     fn foo<A: Debug>(&self, a: &A, b: &impl Debug);
    |                                -- type in trait
 ...
 LL |     fn foo<B: Debug>(&self, a: &impl Debug, b: &B) { }
-   |            -                   ^^^^^^^^^^^ expected type parameter `B`, found type parameter `impl Debug`
-   |            |
+   |            -                   ^^^^^^^^^^^
+   |            |                   |
+   |            |                   expected type parameter `B`, found type parameter `impl Debug`
+   |            |                   help: change the parameter type to match the trait: `&B`
    |            expected type parameter
    |
    = note: expected fn pointer `fn(&(), &B, &impl Debug)`
diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr
index 0bf43a65aa0..bea24339837 100644
--- a/src/test/ui/impl-trait/trait_type.stderr
+++ b/src/test/ui/impl-trait/trait_type.stderr
@@ -2,7 +2,10 @@ error[E0053]: method `fmt` has an incompatible type for trait
   --> $DIR/trait_type.rs:7:21
    |
 LL |    fn fmt(&self, x: &str) -> () { }
-   |                     ^^^^ types differ in mutability
+   |                     ^^^^
+   |                     |
+   |                     types differ in mutability
+   |                     help: change the parameter type to match the trait: `&mut Formatter<'_>`
    |
    = note: expected fn pointer `fn(&MyType, &mut Formatter<'_>) -> Result<(), std::fmt::Error>`
               found fn pointer `fn(&MyType, &str)`
diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr
index 57447fa48aa..6c3651ff121 100644
--- a/src/test/ui/issues/issue-13033.stderr
+++ b/src/test/ui/issues/issue-13033.stderr
@@ -8,7 +8,7 @@ LL |     fn bar(&mut self, other: &dyn Foo) {}
    |                              ^^^^^^^^
    |                              |
    |                              types differ in mutability
-   |                              help: consider changing the mutability to match the trait: `&mut dyn Foo`
+   |                              help: change the parameter type to match the trait: `&mut dyn Foo`
    |
    = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)`
               found fn pointer `fn(&mut Baz, &dyn Foo)`
diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr
index 756301de17c..6f4813ca623 100644
--- a/src/test/ui/issues/issue-20225.stderr
+++ b/src/test/ui/issues/issue-20225.stderr
@@ -4,7 +4,10 @@ error[E0053]: method `call` has an incompatible type for trait
 LL | impl<'a, T> Fn<(&'a T,)> for Foo {
    |          - this type parameter
 LL |   extern "rust-call" fn call(&self, (_,): (T,)) {}
-   |                                           ^^^^ expected `&T`, found type parameter `T`
+   |                                           ^^^^
+   |                                           |
+   |                                           expected `&T`, found type parameter `T`
+   |                                           help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(&Foo, (T,))`
@@ -15,7 +18,10 @@ error[E0053]: method `call_mut` has an incompatible type for trait
 LL | impl<'a, T> FnMut<(&'a T,)> for Foo {
    |          - this type parameter
 LL |   extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {}
-   |                                                   ^^^^ expected `&T`, found type parameter `T`
+   |                                                   ^^^^
+   |                                                   |
+   |                                                   expected `&T`, found type parameter `T`
+   |                                                   help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(&mut Foo, (T,))`
@@ -27,7 +33,10 @@ LL | impl<'a, T> FnOnce<(&'a T,)> for Foo {
    |          - this type parameter
 ...
 LL |   extern "rust-call" fn call_once(self, (_,): (T,)) {}
-   |                                               ^^^^ expected `&T`, found type parameter `T`
+   |                                               ^^^^
+   |                                               |
+   |                                               expected `&T`, found type parameter `T`
+   |                                               help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(Foo, (T,))`
diff --git a/src/test/ui/issues/issue-35869.stderr b/src/test/ui/issues/issue-35869.stderr
index f80561bf6be..c104aa30cb0 100644
--- a/src/test/ui/issues/issue-35869.stderr
+++ b/src/test/ui/issues/issue-35869.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(_: fn(u8) -> ());
    |               ------------ type in trait
 ...
 LL |     fn foo(_: fn(u16) -> ()) {}
-   |               ^^^^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `fn(u8)`
    |
    = note: expected fn pointer `fn(fn(u8))`
               found fn pointer `fn(fn(u16))`
@@ -17,7 +20,10 @@ LL |     fn bar(_: Option<u8>);
    |               ---------- type in trait
 ...
 LL |     fn bar(_: Option<u16>) {}
-   |               ^^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `Option<u8>`
    |
    = note: expected fn pointer `fn(Option<u8>)`
               found fn pointer `fn(Option<u16>)`
@@ -29,7 +35,10 @@ LL |     fn baz(_: (u8, u16));
    |               --------- type in trait
 ...
 LL |     fn baz(_: (u16, u16)) {}
-   |               ^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `(u8, u16)`
    |
    = note: expected fn pointer `fn((u8, _))`
               found fn pointer `fn((u16, _))`
diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr
index e0a3ce922b9..6ce8126b9f9 100644
--- a/src/test/ui/mismatched_types/E0053.stderr
+++ b/src/test/ui/mismatched_types/E0053.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(x: u16);
    |               --- type in trait
 ...
 LL |     fn foo(x: i16) { }
-   |               ^^^ expected `u16`, found `i16`
+   |               ^^^
+   |               |
+   |               expected `u16`, found `i16`
+   |               help: change the parameter type to match the trait: `u16`
    |
    = note: expected fn pointer `fn(u16)`
               found fn pointer `fn(i16)`
@@ -20,7 +23,7 @@ LL |     fn bar(&mut self) { }
    |            ^^^^^^^^^
    |            |
    |            types differ in mutability
-   |            help: consider changing the mutability to match the trait: `&self`
+   |            help: change the self-receiver type to match the trait: `self: &Bar`
    |
    = note: expected fn pointer `fn(&Bar)`
               found fn pointer `fn(&mut Bar)`
diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
index 161843473b6..2ac4d1c33a9 100644
--- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
+++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(x: u16);
    |               --- type in trait
 ...
 LL |     fn foo(x: i16) { }
-   |               ^^^ expected `u16`, found `i16`
+   |               ^^^
+   |               |
+   |               expected `u16`, found `i16`
+   |               help: change the parameter type to match the trait: `u16`
    |
    = note: expected fn pointer `fn(u16)`
               found fn pointer `fn(i16)`
@@ -20,7 +23,7 @@ LL |     fn bar(&mut self, bar: &Bar) { }
    |                            ^^^^
    |                            |
    |                            types differ in mutability
-   |                            help: consider changing the mutability to match the trait: `&mut Bar`
+   |                            help: change the parameter type to match the trait: `&mut Bar`
    |
    = note: expected fn pointer `fn(&mut Bar, &mut Bar)`
               found fn pointer `fn(&mut Bar, &Bar)`
diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr
index ce3528dcc9c..9be07cb1a74 100644
--- a/src/test/ui/wrong-mul-method-signature.stderr
+++ b/src/test/ui/wrong-mul-method-signature.stderr
@@ -2,7 +2,10 @@ error[E0053]: method `mul` has an incompatible type for trait
   --> $DIR/wrong-mul-method-signature.rs:16:21
    |
 LL |     fn mul(self, s: &f64) -> Vec1 {
-   |                     ^^^^ expected `f64`, found `&f64`
+   |                     ^^^^
+   |                     |
+   |                     expected `f64`, found `&f64`
+   |                     help: change the parameter type to match the trait: `f64`
    |
    = note: expected fn pointer `fn(Vec1, f64) -> Vec1`
               found fn pointer `fn(Vec1, &f64) -> Vec1`
@@ -11,7 +14,10 @@ error[E0053]: method `mul` has an incompatible type for trait
   --> $DIR/wrong-mul-method-signature.rs:33:21
    |
 LL |     fn mul(self, s: f64) -> Vec2 {
-   |                     ^^^ expected struct `Vec2`, found `f64`
+   |                     ^^^
+   |                     |
+   |                     expected struct `Vec2`, found `f64`
+   |                     help: change the parameter type to match the trait: `Vec2`
    |
    = note: expected fn pointer `fn(Vec2, Vec2) -> f64`
               found fn pointer `fn(Vec2, f64) -> Vec2`