Skip to content

Commit

Permalink
Suggest changing impl parameter types to match trait
Browse files Browse the repository at this point in the history
This is particularly useful for cases where arbitrary self types are
used, like in custom `Future`s.
  • Loading branch information
estebank committed Apr 8, 2021
1 parent d43ede1 commit 147649d
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 25 deletions.
50 changes: 43 additions & 7 deletions compiler/rustc_typeck/src/check/compare_method.rs
Expand Up @@ -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(
Expand Down Expand Up @@ -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
);
Expand Down
23 changes: 23 additions & 0 deletions 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
}
30 changes: 30 additions & 0 deletions 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`.
6 changes: 4 additions & 2 deletions src/test/ui/compare-method/reordered-type-param.stderr
Expand Up @@ -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
|
Expand Down
6 changes: 4 additions & 2 deletions src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
Expand Up @@ -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)`
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/impl-trait/trait_type.stderr
Expand Up @@ -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)`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-13033.stderr
Expand Up @@ -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)`
Expand Down
15 changes: 12 additions & 3 deletions src/test/ui/issues/issue-20225.stderr
Expand Up @@ -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,))`
Expand All @@ -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,))`
Expand All @@ -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,))`
Expand Down
15 changes: 12 additions & 3 deletions src/test/ui/issues/issue-35869.stderr
Expand Up @@ -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))`
Expand All @@ -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>)`
Expand All @@ -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, _))`
Expand Down
7 changes: 5 additions & 2 deletions src/test/ui/mismatched_types/E0053.stderr
Expand Up @@ -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)`
Expand All @@ -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)`
Expand Down
Expand Up @@ -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)`
Expand All @@ -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)`
Expand Down
10 changes: 8 additions & 2 deletions src/test/ui/wrong-mul-method-signature.stderr
Expand Up @@ -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`
Expand All @@ -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`
Expand Down

0 comments on commit 147649d

Please sign in to comment.