Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest box/pin/arc ing receiver on method calls #83667

Merged
merged 2 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 16 additions & 57 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::astconv::AstConv as _;
use crate::check::cast;
use crate::check::coercion::CoerceMany;
use crate::check::fatally_break_rust;
use crate::check::method::{probe, MethodError, SelfSource};
use crate::check::method::SelfSource;
use crate::check::report_unexpected_variant_res;
use crate::check::BreakableCtxt;
use crate::check::Diverges;
Expand All @@ -30,7 +30,6 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder,
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, QPath};
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
Expand Down Expand Up @@ -461,7 +460,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1
}

fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
fn check_expr_path(
&self,
qpath: &'tcx hir::QPath<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span);
let ty = match res {
Expand Down Expand Up @@ -947,7 +950,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
Err(error) => {
if segment.ident.name != kw::Empty {
self.report_extended_method_error(segment, span, args, rcvr_t, error);
if let Some(mut err) = self.report_method_error(
span,
rcvr_t,
segment.ident,
SelfSource::MethodCall(&args[0]),
error,
Some(args),
) {
err.emit();
}
}
Err(())
}
Expand All @@ -964,59 +976,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}

fn report_extended_method_error(
&self,
segment: &hir::PathSegment<'_>,
span: Span,
args: &'tcx [hir::Expr<'tcx>],
rcvr_t: Ty<'tcx>,
error: MethodError<'tcx>,
) {
let rcvr = &args[0];
let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| {
if let Some(new_rcvr_t) = new_rcvr_t {
if let Ok(pick) = self.lookup_probe(
span,
segment.ident,
new_rcvr_t,
rcvr,
probe::ProbeScope::AllTraits,
) {
debug!("try_alt_rcvr: pick candidate {:?}", pick);
// Make sure the method is defined for the *actual* receiver:
// we don't want to treat `Box<Self>` as a receiver if
// it only works because of an autoderef to `&self`
if pick.autoderefs == 0 {
err.span_label(
pick.item.ident.span,
&format!("the method is available for `{}` here", new_rcvr_t),
);
}
}
}
};

if let Some(mut err) = self.report_method_error(
span,
rcvr_t,
segment.ident,
SelfSource::MethodCall(rcvr),
error,
Some(args),
) {
if let ty::Adt(..) = rcvr_t.kind() {
// 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));
}
err.emit();
}
}

fn check_expr_cast(
&self,
e: &'tcx hir::Expr<'tcx>,
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,12 +905,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

/// Resolves an associated value path into a base type and associated constant, or method
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
pub fn resolve_ty_and_res_ufcs<'b>(
pub fn resolve_ty_and_res_ufcs(
&self,
qpath: &'b QPath<'b>,
qpath: &'tcx QPath<'tcx>,
hir_id: hir::HirId,
span: Span,
) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) {
) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
let (ty, qself, item_segment) = match *qpath {
QPath::Resolved(ref opt_qself, ref path) => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_typeck/src/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>),
Expand All @@ -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<CandidateSource>,
pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
Expand Down
87 changes: 78 additions & 9 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

pub fn report_method_error<'b>(
pub fn report_method_error(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'b>,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
args: Option<&'tcx [hir::Expr<'tcx>]>,
) -> Option<DiagnosticBuilder<'_>> {
Expand Down Expand Up @@ -323,8 +323,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_suggestion(
lit.span,
&format!(
"you must specify a concrete type for \
this numeric value, like `{}`",
"you must specify a concrete type for this numeric value, \
like `{}`",
concrete_type
),
format!("{}_{}", snippet, concrete_type),
Expand Down Expand Up @@ -975,17 +975,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn suggest_traits_to_import<'b>(
fn suggest_traits_to_import(
&self,
err: &mut DiagnosticBuilder<'_>,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'b>,
source: SelfSource<'tcx>,
valid_out_of_scope_traits: Vec<DefId>,
unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)],
) {
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
let mut alt_rcvr_sugg = false;
if let SelfSource::MethodCall(rcvr) = source {
info!(?span, ?item_name, ?rcvr_ty, ?rcvr);
if let ty::Adt(..) = rcvr_ty.kind() {
// 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.
for (rcvr_ty, post) in &[
(rcvr_ty, ""),
(self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "),
(self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"),
] {
for (rcvr_ty, pre) in &[
(self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"),
(self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"),
(self.tcx.mk_diagnostic_item(rcvr_ty, sym::Arc), "Arc::new"),
(self.tcx.mk_diagnostic_item(rcvr_ty, sym::Rc), "Rc::new"),
] {
if let Some(new_rcvr_t) = *rcvr_ty {
if let Ok(pick) = self.lookup_probe(
span,
item_name,
new_rcvr_t,
rcvr,
crate::check::method::probe::ProbeScope::AllTraits,
) {
debug!("try_alt_rcvr: pick candidate {:?}", pick);
// Make sure the method is defined for the *actual* receiver:
// we don't want to treat `Box<Self>` as a receiver if
// it only works because of an autoderef to `&self`
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,
);
// We don't care about the other suggestions.
alt_rcvr_sugg = true;
}
}
}
}
}
}
}
if !alt_rcvr_sugg && self.suggest_valid_traits(err, valid_out_of_scope_traits) {
return;
}

Expand Down Expand Up @@ -1075,6 +1136,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the method might not be found because of this arbitrary self type",
);
}
if alt_rcvr_sugg {
return;
}

if !candidates.is_empty() {
// Sort from most relevant to least relevant.
Expand Down Expand Up @@ -1284,7 +1348,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

/// Checks whether there is a local type somewhere in the chain of
/// autoderefs of `rcvr_ty`.
fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool {
fn type_derefs_to_local(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
source: SelfSource<'tcx>,
) -> bool {
fn is_local(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Adt(def, _) => def.did.is_local(),
Expand All @@ -1310,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum SelfSource<'a> {
QPath(&'a hir::Ty<'a>),
MethodCall(&'a hir::Expr<'a> /* rcvr */),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_pat_tuple_struct(
&self,
pat: &'tcx Pat<'tcx>,
qpath: &hir::QPath<'_>,
qpath: &'tcx hir::QPath<'tcx>,
subpats: &'tcx [&'tcx Pat<'tcx>],
ddpos: Option<usize>,
expected: Ty<'tcx>,
Expand Down
47 changes: 47 additions & 0 deletions src/test/ui/async-await/pin-needed-to-poll.rs
Original file line number Diff line number Diff line change
@@ -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<Self::Output> {
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::Output> {
self.sleep.poll(cx)
//~^ ERROR no method named `poll` found for struct `Sleep` in the current scope
}
}

fn main() {}
22 changes: 22 additions & 0 deletions src/test/ui/async-await/pin-needed-to-poll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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<Self::Output>;
| ---- the method is available for `Pin<&mut Sleep>` here
|
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`.
8 changes: 0 additions & 8 deletions src/test/ui/copy-a-resource.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<Foo>` here
| the method is available for `Rc<Foo>` 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:
Expand Down
8 changes: 0 additions & 8 deletions src/test/ui/derives/derive-assoc-type-not-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ LL | struct NotClone;
...
LL | Bar::<NotClone> { x: 1 }.clone();
| ^^^^^ method cannot be called on `Bar<NotClone>` due to unsatisfied trait bounds
|
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
LL | fn clone(&self) -> Self;
| -----
| |
| the method is available for `Arc<Bar<NotClone>>` here
| the method is available for `Rc<Bar<NotClone>>` here
|
= note: the following trait bounds were not satisfied:
`NotClone: Clone`
Expand Down
Loading