Skip to content

Commit 90c0371

Browse files
authored
Rollup merge of #114469 - estebank:arbitrary-self-types-mut-diff, r=davidtwco
Detect method not found on arbitrary self type with different mutability ``` 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>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` Related to #57994, as one of the presented cases can lead to code like this.
2 parents 128cc06 + 843549e commit 90c0371

13 files changed

+275
-17
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::errors::{
1313
YieldExprOutsideOfGenerator,
1414
};
1515
use crate::fatally_break_rust;
16-
use crate::method::SelfSource;
16+
use crate::method::{MethodCallComponents, SelfSource};
1717
use crate::type_error_struct;
1818
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
1919
use crate::{
@@ -1281,7 +1281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12811281
segment.ident,
12821282
SelfSource::MethodCall(rcvr),
12831283
error,
1284-
Some((rcvr, args)),
1284+
Some(MethodCallComponents { receiver: rcvr, args, full_expr: expr }),
12851285
expected,
12861286
false,
12871287
) {

compiler/rustc_hir_typeck/src/method/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod prelude2021;
77
pub mod probe;
88
mod suggest;
99

10-
pub use self::suggest::SelfSource;
10+
pub use self::suggest::{MethodCallComponents, SelfSource};
1111
pub use self::MethodError::*;
1212

1313
use crate::errors::OpMethodGenericParams;

compiler/rustc_hir_typeck/src/method/suggest.rs

+65-14
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ use rustc_hir::intravisit::Visitor;
5050
use std::cmp::{self, Ordering};
5151
use std::iter;
5252

53+
/// After identifying that `full_expr` is a method call, we use this type to keep the expression's
54+
/// components readily available to us to point at the right place in diagnostics.
55+
#[derive(Debug, Clone, Copy)]
56+
pub struct MethodCallComponents<'tcx> {
57+
pub receiver: &'tcx hir::Expr<'tcx>,
58+
pub args: &'tcx [hir::Expr<'tcx>],
59+
pub full_expr: &'tcx hir::Expr<'tcx>,
60+
}
61+
5362
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5463
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
5564
let tcx = self.tcx;
@@ -115,7 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
115124
item_name: Ident,
116125
source: SelfSource<'tcx>,
117126
error: MethodError<'tcx>,
118-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
127+
args: Option<MethodCallComponents<'tcx>>,
119128
expected: Expectation<'tcx>,
120129
trait_missing_method: bool,
121130
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
@@ -257,18 +266,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
257266
fn suggest_missing_writer(
258267
&self,
259268
rcvr_ty: Ty<'tcx>,
260-
args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]),
269+
args: MethodCallComponents<'tcx>,
261270
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
262271
let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty);
263-
let mut err =
264-
struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str);
272+
let mut err = struct_span_err!(
273+
self.tcx.sess,
274+
args.receiver.span,
275+
E0599,
276+
"cannot write into `{}`",
277+
ty_str
278+
);
265279
err.span_note(
266-
args.0.span,
280+
args.receiver.span,
267281
"must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method",
268282
);
269-
if let ExprKind::Lit(_) = args.0.kind {
283+
if let ExprKind::Lit(_) = args.receiver.kind {
270284
err.span_help(
271-
args.0.span.shrink_to_lo(),
285+
args.receiver.span.shrink_to_lo(),
272286
"a writer is needed before this format string",
273287
);
274288
};
@@ -282,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
282296
rcvr_ty: Ty<'tcx>,
283297
item_name: Ident,
284298
source: SelfSource<'tcx>,
285-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
299+
args: Option<MethodCallComponents<'tcx>>,
286300
sugg_span: Span,
287301
no_match_data: &mut NoMatchData<'tcx>,
288302
expected: Expectation<'tcx>,
@@ -953,6 +967,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
953967

954968
unsatisfied_bounds = true;
955969
}
970+
} else if let ty::Adt(def, targs) = rcvr_ty.kind() && let Some(args) = args {
971+
// This is useful for methods on arbitrary self types that might have a simple
972+
// mutability difference, like calling a method on `Pin<&mut Self>` that is on
973+
// `Pin<&Self>`.
974+
if targs.len() == 1 {
975+
let mut item_segment = hir::PathSegment::invalid();
976+
item_segment.ident = item_name;
977+
for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] {
978+
let new_args = tcx.mk_args_from_iter(
979+
targs
980+
.iter()
981+
.map(|arg| match arg.as_type() {
982+
Some(ty) => ty::GenericArg::from(
983+
t(tcx, tcx.lifetimes.re_erased, ty.peel_refs()),
984+
),
985+
_ => arg,
986+
})
987+
);
988+
let rcvr_ty = Ty::new_adt(tcx, *def, new_args);
989+
if let Ok(method) = self.lookup_method_for_diagnostic(
990+
rcvr_ty,
991+
&item_segment,
992+
span,
993+
args.full_expr,
994+
args.receiver,
995+
) {
996+
err.span_note(
997+
tcx.def_span(method.def_id),
998+
format!("{item_kind} is available for `{rcvr_ty}`"),
999+
);
1000+
}
1001+
}
1002+
}
9561003
}
9571004

9581005
let label_span_not_found = |err: &mut Diagnostic| {
@@ -1111,7 +1158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11111158
span,
11121159
rcvr_ty,
11131160
item_name,
1114-
args.map(|(_, args)| args.len() + 1),
1161+
args.map(|MethodCallComponents { args, .. }| args.len() + 1),
11151162
source,
11161163
no_match_data.out_of_scope_traits.clone(),
11171164
&unsatisfied_predicates,
@@ -1192,7 +1239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11921239
&self,
11931240
rcvr_ty: Ty<'tcx>,
11941241
item_name: Ident,
1195-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
1242+
args: Option<MethodCallComponents<'tcx>>,
11961243
span: Span,
11971244
err: &mut Diagnostic,
11981245
sources: &mut Vec<CandidateSource>,
@@ -1343,7 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13431390
rcvr_ty: Ty<'tcx>,
13441391
source: SelfSource<'tcx>,
13451392
item_name: Ident,
1346-
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
1393+
args: Option<MethodCallComponents<'tcx>>,
13471394
sugg_span: Span,
13481395
) {
13491396
let mut has_unsuggestable_args = false;
@@ -1415,7 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14151462
None
14161463
};
14171464
let mut applicability = Applicability::MachineApplicable;
1418-
let args = if let Some((receiver, args)) = args {
1465+
let args = if let Some(MethodCallComponents { receiver, args, .. }) = args {
14191466
// The first arg is the same kind as the receiver
14201467
let explicit_args = if first_arg.is_some() {
14211468
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
@@ -2995,7 +3042,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
29953042

29963043
fn print_disambiguation_help<'tcx>(
29973044
item_name: Ident,
2998-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
3045+
args: Option<MethodCallComponents<'tcx>>,
29993046
err: &mut Diagnostic,
30003047
trait_name: String,
30013048
rcvr_ty: Ty<'_>,
@@ -3007,7 +3054,11 @@ fn print_disambiguation_help<'tcx>(
30073054
fn_has_self_parameter: bool,
30083055
) {
30093056
let mut applicability = Applicability::MachineApplicable;
3010-
let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
3057+
let (span, sugg) = if let (
3058+
ty::AssocKind::Fn,
3059+
Some(MethodCallComponents { receiver, args, .. }),
3060+
) = (kind, args)
3061+
{
30113062
let args = format!(
30123063
"({}{})",
30133064
rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Related to #57994.
2+
use std::pin::Pin;
3+
struct S;
4+
5+
impl S {
6+
fn x(self: Pin<&mut Self>) {} //~ NOTE method is available for `Pin<&mut S>`
7+
fn y(self: Pin<&Self>) {} //~ NOTE method is available for `Pin<&S>`
8+
}
9+
10+
fn main() {
11+
Pin::new(&S).x(); //~ ERROR no method named `x` found for struct `Pin<&S>` in the current scope
12+
Pin::new(&mut S).y(); //~ ERROR no method named `y` found for struct `Pin<&mut S>` in the current scope
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0599]: no method named `x` found for struct `Pin<&S>` in the current scope
2+
--> $DIR/arbitrary_self_type_mut_difference.rs:11:18
3+
|
4+
LL | Pin::new(&S).x();
5+
| ^ help: there is a method with a similar name: `y`
6+
|
7+
note: method is available for `Pin<&mut S>`
8+
--> $DIR/arbitrary_self_type_mut_difference.rs:6:5
9+
|
10+
LL | fn x(self: Pin<&mut Self>) {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error[E0599]: no method named `y` found for struct `Pin<&mut S>` in the current scope
14+
--> $DIR/arbitrary_self_type_mut_difference.rs:12:22
15+
|
16+
LL | Pin::new(&mut S).y();
17+
| ^ help: there is a method with a similar name: `x`
18+
|
19+
note: method is available for `Pin<&S>`
20+
--> $DIR/arbitrary_self_type_mut_difference.rs:7:5
21+
|
22+
LL | fn y(self: Pin<&Self>) {}
23+
| ^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0599`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
mod first {
4+
trait Foo { fn m(self: Box<Self>); }
5+
fn foo<T: Foo>(a: T) {
6+
Box::new(a).m(); //~ ERROR no method named `m` found
7+
}
8+
}
9+
mod second {
10+
use std::sync::Arc;
11+
trait Bar { fn m(self: Arc<Self>); }
12+
fn bar(b: impl Bar) {
13+
Arc::new(b).m(); //~ ERROR no method named `m` found
14+
}
15+
}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
mod first {
4+
trait Foo { fn m(self: Box<Self>); }
5+
fn foo<T: Foo>(a: T) {
6+
a.m(); //~ ERROR no method named `m` found
7+
}
8+
}
9+
mod second {
10+
use std::sync::Arc;
11+
trait Bar { fn m(self: Arc<Self>); }
12+
fn bar(b: impl Bar) {
13+
b.m(); //~ ERROR no method named `m` found
14+
}
15+
}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0599]: no method named `m` found for type parameter `T` in the current scope
2+
--> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:6:11
3+
|
4+
LL | trait Foo { fn m(self: Box<Self>); }
5+
| - --------- the method might not be found because of this arbitrary self type
6+
| |
7+
| the method is available for `Box<T>` here
8+
LL | fn foo<T: Foo>(a: T) {
9+
| - method `m` not found for this type parameter
10+
LL | a.m();
11+
| ^ method not found in `T`
12+
...
13+
LL | trait Bar { fn m(self: Arc<Self>); }
14+
| --------- the method might not be found because of this arbitrary self type
15+
|
16+
help: consider wrapping the receiver expression with the appropriate type
17+
|
18+
LL | Box::new(a).m();
19+
| +++++++++ +
20+
21+
error[E0599]: no method named `m` found for type parameter `impl Bar` in the current scope
22+
--> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:13:11
23+
|
24+
LL | trait Foo { fn m(self: Box<Self>); }
25+
| --------- the method might not be found because of this arbitrary self type
26+
...
27+
LL | trait Bar { fn m(self: Arc<Self>); }
28+
| - --------- the method might not be found because of this arbitrary self type
29+
| |
30+
| the method is available for `Arc<impl Bar>` here
31+
LL | fn bar(b: impl Bar) {
32+
| -------- method `m` not found for this type parameter
33+
LL | b.m();
34+
| ^ method not found in `impl Bar`
35+
|
36+
help: consider wrapping the receiver expression with the appropriate type
37+
|
38+
LL | Arc::new(b).m();
39+
| +++++++++ +
40+
41+
error: aborting due to 2 previous errors
42+
43+
For more information about this error, try `rustc --explain E0599`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// run-rustfix
2+
use std::pin::Pin;
3+
struct S;
4+
5+
impl S {
6+
fn x(self: Pin<&mut Self>) {
7+
}
8+
}
9+
10+
fn main() {
11+
Pin::new(&mut S).x(); //~ ERROR no method named `x` found
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// run-rustfix
2+
use std::pin::Pin;
3+
struct S;
4+
5+
impl S {
6+
fn x(self: Pin<&mut Self>) {
7+
}
8+
}
9+
10+
fn main() {
11+
S.x(); //~ ERROR no method named `x` found
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0599]: no method named `x` found for struct `S` in the current scope
2+
--> $DIR/arbitrary_self_types_needing_mut_pin.rs:11:7
3+
|
4+
LL | struct S;
5+
| -------- method `x` not found for this struct
6+
...
7+
LL | fn x(self: Pin<&mut Self>) {
8+
| - the method is available for `Pin<&mut S>` here
9+
...
10+
LL | S.x();
11+
| ^ method not found in `S`
12+
|
13+
help: consider wrapping the receiver expression with the appropriate type
14+
|
15+
LL | Pin::new(&mut S).x();
16+
| +++++++++++++ +
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0599`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::pin::Pin;
2+
struct S;
3+
4+
impl S {
5+
fn x(self: Pin<&mut Self>) {
6+
}
7+
}
8+
9+
fn main() {
10+
Pin::new(S).x();
11+
//~^ ERROR the trait bound `S: Deref` is not satisfied
12+
//~| ERROR no method named `x` found for struct `Pin` in the current scope
13+
}

0 commit comments

Comments
 (0)