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

Detect method not found on arbitrary self type with different mutability #114469

Merged
merged 3 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1293,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
segment.ident,
SelfSource::MethodCall(rcvr),
error,
Some((rcvr, args)),
Some((rcvr, args, expr)),
expected,
false,
) {
Expand Down
45 changes: 36 additions & 9 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: Ident,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
expected: Expectation<'tcx>,
trait_missing_method: bool,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
Expand Down Expand Up @@ -257,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn suggest_missing_writer(
&self,
rcvr_ty: Ty<'tcx>,
args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]),
args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>),
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty);
let mut err =
Expand All @@ -282,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'tcx>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
sugg_span: Span,
no_match_data: &mut NoMatchData<'tcx>,
expected: Expectation<'tcx>,
Expand Down Expand Up @@ -953,6 +953,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

unsatisfied_bounds = true;
}
} else if let ty::Adt(def, targs) = rcvr_ty.kind() && let Some((rcvr, _, expr)) = args {
// This is useful for methods on arbitrary self types that might have a simple
// mutability difference, like calling a method on `Pin<&mut Self>` that is on
// `Pin<&Self>`.
if targs.len() == 1 {
let mut item_segment = hir::PathSegment::invalid();
item_segment.ident = item_name;
for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] {
let new_args = tcx.mk_args_from_iter(
targs
.iter()
.map(|arg| match arg.as_type() {
Some(ty) => ty::GenericArg::from(
t(tcx, tcx.lifetimes.re_erased, ty.peel_refs()),
),
_ => arg,
})
);
let rcvr_ty = Ty::new_adt(tcx, *def, new_args);
if let Ok(method) = self.lookup_method_for_diagnostic(rcvr_ty, &item_segment, span, expr, rcvr) {
err.span_note(
tcx.def_span(method.def_id),
format!("{item_kind} is available for `{rcvr_ty}`"),
);
}
}
}
}

let label_span_not_found = |err: &mut Diagnostic| {
Expand Down Expand Up @@ -1111,7 +1138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
rcvr_ty,
item_name,
args.map(|(_, args)| args.len() + 1),
args.map(|(_, args, _)| args.len() + 1),
source,
no_match_data.out_of_scope_traits.clone(),
&unsatisfied_predicates,
Expand Down Expand Up @@ -1192,7 +1219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
span: Span,
err: &mut Diagnostic,
sources: &mut Vec<CandidateSource>,
Expand Down Expand Up @@ -1343,7 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
rcvr_ty: Ty<'tcx>,
source: SelfSource<'tcx>,
item_name: Ident,
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
sugg_span: Span,
) {
let mut has_unsuggestable_args = false;
Expand Down Expand Up @@ -1415,7 +1442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
};
let mut applicability = Applicability::MachineApplicable;
let args = if let Some((receiver, args)) = args {
let args = if let Some((receiver, args, _)) = args {
// The first arg is the same kind as the receiver
let explicit_args = if first_arg.is_some() {
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
Expand Down Expand Up @@ -2995,7 +3022,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {

fn print_disambiguation_help<'tcx>(
item_name: Ident,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
err: &mut Diagnostic,
trait_name: String,
rcvr_ty: Ty<'_>,
Expand All @@ -3007,7 +3034,7 @@ fn print_disambiguation_help<'tcx>(
fn_has_self_parameter: bool,
) {
let mut applicability = Applicability::MachineApplicable;
let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args, _))) = (kind, args) {
let args = format!(
"({}{})",
rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/self/arbitrary_self_type_mut_difference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Related to #57994.
use std::pin::Pin;
struct S;

impl S {
fn x(self: Pin<&mut Self>) {} //~ NOTE method is available for `Pin<&mut S>`
fn y(self: Pin<&Self>) {} //~ NOTE method is available for `Pin<&S>`
}

fn main() {
Pin::new(&S).x(); //~ ERROR no method named `x` found for struct `Pin<&S>` in the current scope
Pin::new(&mut S).y(); //~ ERROR no method named `y` found for struct `Pin<&mut S>` in the current scope
}
27 changes: 27 additions & 0 deletions tests/ui/self/arbitrary_self_type_mut_difference.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0599]: no method named `x` found for struct `Pin<&S>` in the current scope
--> $DIR/arbitrary_self_type_mut_difference.rs:11:18
|
LL | Pin::new(&S).x();
| ^ help: there is a method with a similar name: `y`
|
note: method is available for `Pin<&mut S>`
--> $DIR/arbitrary_self_type_mut_difference.rs:6:5
|
LL | fn x(self: Pin<&mut Self>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0599]: no method named `y` found for struct `Pin<&mut S>` in the current scope
--> $DIR/arbitrary_self_type_mut_difference.rs:12:22
|
LL | Pin::new(&mut S).y();
| ^ help: there is a method with a similar name: `x`
|
note: method is available for `Pin<&S>`
--> $DIR/arbitrary_self_type_mut_difference.rs:7:5
|
LL | fn y(self: Pin<&Self>) {}
| ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0599`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// run-rustfix
#![allow(dead_code)]
mod first {
trait Foo { fn m(self: Box<Self>); }
fn foo<T: Foo>(a: T) {
Box::new(a).m(); //~ ERROR no method named `m` found
}
}
mod second {
use std::sync::Arc;
trait Bar { fn m(self: Arc<Self>); }
fn bar(b: impl Bar) {
Arc::new(b).m(); //~ ERROR no method named `m` found
}
}

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// run-rustfix
#![allow(dead_code)]
mod first {
trait Foo { fn m(self: Box<Self>); }
fn foo<T: Foo>(a: T) {
a.m(); //~ ERROR no method named `m` found
}
}
mod second {
use std::sync::Arc;
trait Bar { fn m(self: Arc<Self>); }
fn bar(b: impl Bar) {
b.m(); //~ ERROR no method named `m` found
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error[E0599]: no method named `m` found for type parameter `T` in the current scope
--> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:6:11
|
LL | trait Foo { fn m(self: Box<Self>); }
| - --------- the method might not be found because of this arbitrary self type
| |
| the method is available for `Box<T>` here
LL | fn foo<T: Foo>(a: T) {
| - method `m` not found for this type parameter
LL | a.m();
| ^ method not found in `T`
...
LL | trait Bar { fn m(self: Arc<Self>); }
| --------- the method might not be found because of this arbitrary self type
|
help: consider wrapping the receiver expression with the appropriate type
|
LL | Box::new(a).m();
| +++++++++ +

error[E0599]: no method named `m` found for type parameter `impl Bar` in the current scope
--> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:13:11
|
LL | trait Foo { fn m(self: Box<Self>); }
| --------- the method might not be found because of this arbitrary self type
...
LL | trait Bar { fn m(self: Arc<Self>); }
| - --------- the method might not be found because of this arbitrary self type
| |
| the method is available for `Arc<impl Bar>` here
LL | fn bar(b: impl Bar) {
| -------- method `m` not found for this type parameter
LL | b.m();
| ^ method not found in `impl Bar`
|
help: consider wrapping the receiver expression with the appropriate type
|
LL | Arc::new(b).m();
| +++++++++ +

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0599`.
12 changes: 12 additions & 0 deletions tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// run-rustfix
use std::pin::Pin;
struct S;

impl S {
fn x(self: Pin<&mut Self>) {
}
}

fn main() {
Pin::new(&mut S).x(); //~ ERROR no method named `x` found
}
12 changes: 12 additions & 0 deletions tests/ui/self/arbitrary_self_types_needing_mut_pin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// run-rustfix
use std::pin::Pin;
struct S;

impl S {
fn x(self: Pin<&mut Self>) {
}
}

fn main() {
S.x(); //~ ERROR no method named `x` found
}
20 changes: 20 additions & 0 deletions tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0599]: no method named `x` found for struct `S` in the current scope
--> $DIR/arbitrary_self_types_needing_mut_pin.rs:11:7
|
LL | struct S;
| -------- method `x` not found for this struct
...
LL | fn x(self: Pin<&mut Self>) {
| - the method is available for `Pin<&mut S>` here
...
LL | S.x();
| ^ method not found in `S`
|
help: consider wrapping the receiver expression with the appropriate type
|
LL | Pin::new(&mut S).x();
| +++++++++++++ +

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
13 changes: 13 additions & 0 deletions tests/ui/self/arbitrary_self_types_pin_needing_borrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::pin::Pin;
struct S;

impl S {
fn x(self: Pin<&mut Self>) {
}
}

fn main() {
Pin::new(S).x();
//~^ ERROR the trait bound `S: Deref` is not satisfied
//~| ERROR no method named `x` found for struct `Pin` in the current scope
}
33 changes: 33 additions & 0 deletions tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0277]: the trait bound `S: Deref` is not satisfied
--> $DIR/arbitrary_self_types_pin_needing_borrow.rs:10:14
|
LL | Pin::new(S).x();
| -------- ^ the trait `Deref` is not implemented for `S`
| |
| required by a bound introduced by this call
|
note: required by a bound in `Pin::<P>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL
help: consider borrowing here
|
LL | Pin::new(&S).x();
| +
LL | Pin::new(&mut S).x();
| ++++

error[E0599]: no method named `x` found for struct `Pin` in the current scope
--> $DIR/arbitrary_self_types_pin_needing_borrow.rs:10:17
|
LL | Pin::new(S).x();
| ^ method not found in `Pin<S>`
|
note: method is available for `Pin<&mut S>`
--> $DIR/arbitrary_self_types_pin_needing_borrow.rs:5:5
|
LL | fn x(self: Pin<&mut Self>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.