From 6f2d8a018e4ff062032608a30615129230490b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 29 Mar 2021 18:14:44 -0700 Subject: [PATCH] Suggest box/pin/arc ing receiver on method calls --- compiler/rustc_typeck/src/check/expr.rs | 35 +++++++++++--- compiler/rustc_typeck/src/check/method/mod.rs | 2 + src/test/ui/async-await/pin-needed-to-poll.rs | 47 +++++++++++++++++++ .../ui/async-await/pin-needed-to-poll.stderr | 25 ++++++++++ src/test/ui/copy-a-resource.stderr | 8 ---- .../derives/derive-assoc-type-not-impl.stderr | 8 ---- src/test/ui/issues/issue-2823.stderr | 8 ---- src/test/ui/issues/issue-69725.stderr | 8 ---- src/test/ui/non-copyable-void.stderr | 8 ---- src/test/ui/noncopyable-class.stderr | 8 ---- ...point-at-arbitrary-self-type-method.stderr | 5 ++ ...at-arbitrary-self-type-trait-method.stderr | 5 ++ .../missing-lifetimes-in-signature.nll.stderr | 11 ----- ...licitly-unimplemented-error-message.stderr | 8 ---- src/test/ui/union/union-derive-clone.stderr | 8 ---- src/test/ui/unique-object-noncopyable.stderr | 8 ---- src/test/ui/unique-pinned-nocopy.stderr | 8 ---- 17 files changed, 113 insertions(+), 97 deletions(-) create mode 100644 src/test/ui/async-await/pin-needed-to-poll.rs create mode 100644 src/test/ui/async-await/pin-needed-to-poll.stderr delete mode 100644 src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 8951c08bd33..9a2e933eb0b 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -973,7 +973,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: MethodError<'tcx>, ) { let rcvr = &args[0]; - let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { + let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t, pre: &str, post: &str| { if let Some(new_rcvr_t) = new_rcvr_t { if let Ok(pick) = self.lookup_probe( span, @@ -986,11 +986,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure the method is defined for the *actual* receiver: // we don't want to treat `Box` as a receiver if // it only works because of an autoderef to `&self` - if pick.autoderefs == 0 { + if pick.autoderefs == 0 + // We don't want to suggest a container type when the missing method is + // `.clone()`, otherwise we'd suggest `Arc::new(foo).clone()`, which is + // far from what the user really wants. + && Some(pick.item.container.id()) != self.tcx.lang_items().clone_trait() + { err.span_label( pick.item.ident.span, &format!("the method is available for `{}` here", new_rcvr_t), ); + err.multipart_suggestion( + "consider wrapping the receiver expression with the appropriate type", + vec![ + (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), + (rcvr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); } } } @@ -1008,10 +1021,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Try alternative arbitrary self types that could fulfill this call. // FIXME: probe for all types that *could* be arbitrary self-types, not // just this list. - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox)); - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::Pin)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc)); + for (rcvr_t, post) in &[ + (rcvr_t, ""), + (self.tcx.mk_mut_ref(&ty::ReErased, rcvr_t), "&mut "), + (self.tcx.mk_imm_ref(&ty::ReErased, rcvr_t), "&"), + ] { + for (rcvr_t, pre) in &[ + (self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox), "Box::new"), + (self.tcx.mk_lang_item(rcvr_t, LangItem::Pin), "Pin::new"), + (self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc), "Arc::new"), + (self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc), "Rc::new"), + ] { + try_alt_rcvr(&mut err, *rcvr_t, pre, post); + } + } } err.emit(); } diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index d6fa6bf0067..c74fd25f76d 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -45,6 +45,7 @@ pub struct MethodCallee<'tcx> { pub sig: ty::FnSig<'tcx>, } +#[derive(Debug)] pub enum MethodError<'tcx> { // Did not find an applicable method, but we did find various near-misses that may work. NoMatch(NoMatchData<'tcx>), @@ -66,6 +67,7 @@ pub enum MethodError<'tcx> { // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which // could lead to matches if satisfied, and a list of not-in-scope traits which may work. +#[derive(Debug)] pub struct NoMatchData<'tcx> { pub static_candidates: Vec, pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, diff --git a/src/test/ui/async-await/pin-needed-to-poll.rs b/src/test/ui/async-await/pin-needed-to-poll.rs new file mode 100644 index 00000000000..0d1fe684f60 --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll.rs @@ -0,0 +1,47 @@ +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +struct Sleep; + +impl Future for Sleep { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } +} + +impl Drop for Sleep { + fn drop(&mut self) {} +} + +fn sleep() -> Sleep { + Sleep +} + + +struct MyFuture { + sleep: Sleep, +} + +impl MyFuture { + fn new() -> Self { + Self { + sleep: sleep(), + } + } +} + +impl Future for MyFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.sleep.poll(cx) + //~^ ERROR no method named `poll` found for struct `Sleep` in the current scope + } +} + +fn main() {} diff --git a/src/test/ui/async-await/pin-needed-to-poll.stderr b/src/test/ui/async-await/pin-needed-to-poll.stderr new file mode 100644 index 00000000000..0e3716d6156 --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll.stderr @@ -0,0 +1,25 @@ +error[E0599]: no method named `poll` found for struct `Sleep` in the current scope + --> $DIR/pin-needed-to-poll.rs:42:20 + | +LL | struct Sleep; + | ------------- method `poll` not found for this +... +LL | self.sleep.poll(cx) + | ^^^^ method not found in `Sleep` + | + ::: $SRC_DIR/core/src/future/future.rs:LL:COL + | +LL | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; + | ---- the method is available for `Pin<&mut Sleep>` here + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `poll`, perhaps you need to implement it: + candidate #1: `Future` +help: consider wrapping the receiver expression with the appropriate type + | +LL | Pin::new(&mut self.sleep).poll(cx) + | ^^^^^^^^^^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index 36cf57bd3c5..79095452f9d 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -6,14 +6,6 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc` here - | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.stderr b/src/test/ui/derives/derive-assoc-type-not-impl.stderr index ffee7004f8f..fd993d0f9d8 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -12,14 +12,6 @@ LL | struct NotClone; ... LL | Bar:: { x: 1 }.clone(); | ^^^^^ method cannot be called on `Bar` due to unsatisfied trait bounds - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc>` here - | the method is available for `Rc>` here | = note: the following trait bounds were not satisfied: `NotClone: Clone` diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr index e044352e954..b3bc946292f 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -6,14 +6,6 @@ LL | struct C { ... LL | let _d = c.clone(); | ^^^^^ method not found in `C` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc` here - | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/issues/issue-69725.stderr b/src/test/ui/issues/issue-69725.stderr index 48c71d76af0..4dd6b4bbb68 100644 --- a/src/test/ui/issues/issue-69725.stderr +++ b/src/test/ui/issues/issue-69725.stderr @@ -8,14 +8,6 @@ LL | let _ = Struct::::new().clone(); | LL | pub struct Struct(A); | ------------------------ doesn't satisfy `Struct: Clone` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc>` here - | the method is available for `Rc>` here | = note: the following trait bounds were not satisfied: `A: Clone` diff --git a/src/test/ui/non-copyable-void.stderr b/src/test/ui/non-copyable-void.stderr index 8395a3a0563..99af04e7cd9 100644 --- a/src/test/ui/non-copyable-void.stderr +++ b/src/test/ui/non-copyable-void.stderr @@ -3,14 +3,6 @@ error[E0599]: no method named `clone` found for enum `c_void` in the current sco | LL | let _z = (*y).clone(); | ^^^^^ method not found in `c_void` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc` here - | the method is available for `Rc` here error: aborting due to previous error diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr index b8e467d8402..4674c16eb43 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -6,14 +6,6 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc` here - | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr index 2954a499c18..b804ddfb024 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr @@ -9,6 +9,11 @@ LL | fn foo(self: Box) {} ... LL | A.foo(); | ^^^ method not found in `A` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Box::new(A).foo(); + | ^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr index 89fe84c0d2d..e1ed0e42f98 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr @@ -10,6 +10,11 @@ LL | struct A; ... LL | A.foo() | ^^^ method not found in `A` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Box::new(A).foo() + | ^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr deleted file mode 100644 index 916a6c2bf12..00000000000 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/missing-lifetimes-in-signature.rs:36:11 - | -LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ - | - ^^ undeclared lifetime - | | - | help: consider introducing lifetime `'a` here: `'a,` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr index d39daaba206..01e36a4a62a 100644 --- a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr +++ b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr @@ -6,14 +6,6 @@ LL | struct Qux; ... LL | Qux.clone(); | ^^^^^ method not found in `Qux` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc` here - | the method is available for `Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index a793bf58d23..546394664df 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -25,14 +25,6 @@ LL | struct CloneNoCopy; ... LL | let w = u.clone(); | ^^^^^ method cannot be called on `U5` due to unsatisfied trait bounds - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc>` here - | the method is available for `Rc>` here | = note: the following trait bounds were not satisfied: `CloneNoCopy: Copy` diff --git a/src/test/ui/unique-object-noncopyable.stderr b/src/test/ui/unique-object-noncopyable.stderr index 4bbacfc0a8b..6a355dd2562 100644 --- a/src/test/ui/unique-object-noncopyable.stderr +++ b/src/test/ui/unique-object-noncopyable.stderr @@ -10,14 +10,6 @@ LL | trait Foo { LL | let _z = y.clone(); | ^^^^^ method cannot be called on `Box` due to unsatisfied trait bounds | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc>` here - | the method is available for `Rc>` here - | ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | / pub struct Box< diff --git a/src/test/ui/unique-pinned-nocopy.stderr b/src/test/ui/unique-pinned-nocopy.stderr index dd0b7fc5489..a4421bcf809 100644 --- a/src/test/ui/unique-pinned-nocopy.stderr +++ b/src/test/ui/unique-pinned-nocopy.stderr @@ -7,14 +7,6 @@ LL | struct R { LL | let _j = i.clone(); | ^^^^^ method cannot be called on `Box` due to unsatisfied trait bounds | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc>` here - | the method is available for `Rc>` here - | ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | / pub struct Box<