Skip to content

Commit

Permalink
Recurse over the method chain and maintain a stack to peek at previou…
Browse files Browse the repository at this point in the history
…s receiver to align spans
  • Loading branch information
iSwapna committed Nov 10, 2023
1 parent d42d73b commit 56a109d
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
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

0 comments on commit 56a109d

Please sign in to comment.