Skip to content

Commit 6d11b2f

Browse files
committed
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.
1 parent c435af0 commit 6d11b2f

File tree

4 files changed

+77
-10
lines changed

4 files changed

+77
-10
lines changed

Diff for: compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12931293
segment.ident,
12941294
SelfSource::MethodCall(rcvr),
12951295
error,
1296-
Some((rcvr, args)),
1296+
Some((rcvr, args, expr)),
12971297
expected,
12981298
false,
12991299
) {

Diff for: compiler/rustc_hir_typeck/src/method/suggest.rs

+36-9
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
115115
item_name: Ident,
116116
source: SelfSource<'tcx>,
117117
error: MethodError<'tcx>,
118-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
118+
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
119119
expected: Expectation<'tcx>,
120120
trait_missing_method: bool,
121121
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
@@ -257,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
257257
fn suggest_missing_writer(
258258
&self,
259259
rcvr_ty: Ty<'tcx>,
260-
args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]),
260+
args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>),
261261
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
262262
let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty);
263263
let mut err =
@@ -282,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
282282
rcvr_ty: Ty<'tcx>,
283283
item_name: Ident,
284284
source: SelfSource<'tcx>,
285-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
285+
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
286286
sugg_span: Span,
287287
no_match_data: &mut NoMatchData<'tcx>,
288288
expected: Expectation<'tcx>,
@@ -953,6 +953,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
953953

954954
unsatisfied_bounds = true;
955955
}
956+
} else if let ty::Adt(def, targs) = rcvr_ty.kind() && let Some((rcvr, _, expr)) = args {
957+
// This is useful for methods on arbitrary self types that might have a simple
958+
// mutability difference, like calling a method on `Pin<&mut Self>` that is on
959+
// `Pin<&Self>`.
960+
if targs.len() == 1 {
961+
let mut item_segment = hir::PathSegment::invalid();
962+
item_segment.ident = item_name;
963+
for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] {
964+
let new_args = tcx.mk_args_from_iter(
965+
targs
966+
.iter()
967+
.map(|arg| match arg.as_type() {
968+
Some(ty) => ty::GenericArg::from(
969+
t(tcx, tcx.lifetimes.re_erased, ty.peel_refs()),
970+
),
971+
_ => arg,
972+
})
973+
);
974+
let rcvr_ty = Ty::new_adt(tcx, *def, new_args);
975+
if let Ok(method) = self.lookup_method_for_diagnostic(rcvr_ty, &item_segment, span, expr, rcvr) {
976+
err.span_note(
977+
tcx.def_span(method.def_id),
978+
format!("{item_kind} is available for `{rcvr_ty}`"),
979+
);
980+
}
981+
}
982+
}
956983
}
957984

958985
let label_span_not_found = |err: &mut Diagnostic| {
@@ -1111,7 +1138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11111138
span,
11121139
rcvr_ty,
11131140
item_name,
1114-
args.map(|(_, args)| args.len() + 1),
1141+
args.map(|(_, args, _)| args.len() + 1),
11151142
source,
11161143
no_match_data.out_of_scope_traits.clone(),
11171144
&unsatisfied_predicates,
@@ -1192,7 +1219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11921219
&self,
11931220
rcvr_ty: Ty<'tcx>,
11941221
item_name: Ident,
1195-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
1222+
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
11961223
span: Span,
11971224
err: &mut Diagnostic,
11981225
sources: &mut Vec<CandidateSource>,
@@ -1343,7 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13431370
rcvr_ty: Ty<'tcx>,
13441371
source: SelfSource<'tcx>,
13451372
item_name: Ident,
1346-
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
1373+
args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
13471374
sugg_span: Span,
13481375
) {
13491376
let mut has_unsuggestable_args = false;
@@ -1415,7 +1442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14151442
None
14161443
};
14171444
let mut applicability = Applicability::MachineApplicable;
1418-
let args = if let Some((receiver, args)) = args {
1445+
let args = if let Some((receiver, args, _)) = args {
14191446
// The first arg is the same kind as the receiver
14201447
let explicit_args = if first_arg.is_some() {
14211448
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
@@ -2995,7 +3022,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
29953022

29963023
fn print_disambiguation_help<'tcx>(
29973024
item_name: Ident,
2998-
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
3025+
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], &'tcx hir::Expr<'tcx>)>,
29993026
err: &mut Diagnostic,
30003027
trait_name: String,
30013028
rcvr_ty: Ty<'_>,
@@ -3007,7 +3034,7 @@ fn print_disambiguation_help<'tcx>(
30073034
fn_has_self_parameter: bool,
30083035
) {
30093036
let mut applicability = Applicability::MachineApplicable;
3010-
let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
3037+
let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args, _))) = (kind, args) {
30113038
let args = format!(
30123039
"({}{})",
30133040
rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),

Diff for: tests/ui/self/arbitrary_self_type_mut_difference.rs

+13
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+
}
+27
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`.

0 commit comments

Comments
 (0)