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.
This commit is contained in:
Esteban Küber 2023-08-04 15:29:22 +00:00
parent c435af0d5c
commit 6d11b2f2af
4 changed files with 77 additions and 10 deletions

View file

@ -1293,7 +1293,7 @@ fn check_method_call(
segment.ident,
SelfSource::MethodCall(rcvr),
error,
Some((rcvr, args)),
Some((rcvr, args, expr)),
expected,
false,
) {

View file

@ -115,7 +115,7 @@ pub fn report_method_error(
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>> {
@ -257,7 +257,7 @@ pub fn report_method_error(
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 =
@ -282,7 +282,7 @@ pub fn report_no_match_method_error(
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>,
@ -953,6 +953,33 @@ trait bound{s}",
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| {
@ -1111,7 +1138,7 @@ trait bound{s}",
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,
@ -1192,7 +1219,7 @@ fn note_candidates_on_method_error(
&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>,
@ -1343,7 +1370,7 @@ fn suggest_associated_call_syntax(
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;
@ -1415,7 +1442,7 @@ fn suggest_associated_call_syntax(
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<_>>()
@ -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<'_>,
@ -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()),

View file

@ -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
}

View file

@ -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`.