Skip to content
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
36 changes: 34 additions & 2 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
probe.is_ok()
});

self.note_internal_mutation_in_method(
&mut err,
rcvr_expr,
Expand Down Expand Up @@ -1240,7 +1239,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}

// If an appropriate error source is not found, check method chain for possible candiates
if unsatisfied_predicates.is_empty() && let Mode::MethodCall = mode && let SelfSource::MethodCall(mut source_expr) = source {
let mut stack_methods = vec![];
while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, method_span) =
source_expr.kind
{
// Pop the matching receiver, to align on it's notional span
if let Some(prev_match) = stack_methods.pop() {
err.span_label(method_span, format!("{item_kind} `{item_name}` is available on `{prev_match}`"));
}
let rcvr_ty = self.resolve_vars_if_possible(
self.typeck_results
.borrow()
.expr_ty_adjusted_opt(rcvr_expr)
.unwrap_or(Ty::new_misc_error(self.tcx)),);

for _matched_method in self.probe_for_name_many(
Mode::MethodCall,
item_name,
None,
IsSuggestion(true),
rcvr_ty,
source_expr.hir_id,
ProbeScope::TraitsInScope,) {
// found a match, push to stack
stack_methods.push(rcvr_ty);
}
source_expr = rcvr_expr;
}
// If there is a match at the start of the chain, add a label for it too!
if let Some(prev_match) = stack_methods.pop() {
err.span_label(source_expr.span, format!("{item_kind} `{item_name}` is available on `{prev_match}`"));
}
}
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
return Some(err);
}
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/structs/method-chain-expression-failure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
struct A;
struct B;
struct C;
struct D;
struct E;

impl A {
fn b(&self) -> B { B }
fn foo(&self) {}
}

impl B {
fn c(&self) -> C { C }
}

impl C {
fn d(&self) -> D { D }
fn foo(&self) {}
}

impl D {
fn e(&self) -> E { E }
}

impl E {
fn f(&self) {}
}
fn main() {
A.b().c().d().e().foo();
//~^ ERROR no method named `foo` found for struct `E` in the current scope
}
15 changes: 15 additions & 0 deletions tests/ui/structs/method-chain-expression-failure.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0599]: no method named `foo` found for struct `E` in the current scope
--> $DIR/method-chain-expression-failure.rs:29:23
|
LL | struct E;
| -------- method `foo` not found for this struct
...
LL | A.b().c().d().e().foo();
| - --- ^^^ method not found in `E`
| | |
| | method `foo` is available on `&C`
| method `foo` is available on `&A`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ error[E0599]: no method named `sort` found for unit type `()` in the current sco
--> $DIR/chain-method-call-mutation-in-place.rs:3:72
|
LL | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
| ^^^^ method not found in `()`
| ------------- --------------------- ^^^^ method not found in `()`
| | |
| | method `sort` is available on `&mut [i32]`
| method `sort` is available on `Vec<i32>`
|
note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains.
--> $DIR/chain-method-call-mutation-in-place.rs:3:53
Expand Down