about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSwapna Iyer <swapna.iyer@gmail.com>2023-11-10 13:00:27 -0800
committerSwapna Iyer <swapna.iyer@gmail.com>2023-11-10 13:00:27 -0800
commit56a109d15b5c538bb0f98739c274aa4d81d38f1a (patch)
tree753081e2c9f9d66027953a473118ac111c77e5c1
parentd42d73b144fbb6ccc9684b197a3b6ed53592d09b (diff)
downloadrust-56a109d15b5c538bb0f98739c274aa4d81d38f1a.tar.gz
rust-56a109d15b5c538bb0f98739c274aa4d81d38f1a.zip
Recurse over the method chain and maintain a stack to peek at previous receiver to align spans
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs36
-rw-r--r--tests/ui/structs/method-chain-expression-failure.rs31
-rw-r--r--tests/ui/structs/method-chain-expression-failure.stderr15
-rw-r--r--tests/ui/suggestions/chain-method-call-mutation-in-place.stderr5
4 files changed, 84 insertions, 3 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index b1a2df8ace4..bd40e6c1047 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -481,7 +481,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
                 probe.is_ok()
             });
-
             self.note_internal_mutation_in_method(
                 &mut err,
                 rcvr_expr,
@@ -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);
     }
diff --git a/tests/ui/structs/method-chain-expression-failure.rs b/tests/ui/structs/method-chain-expression-failure.rs
new file mode 100644
index 00000000000..43ebc5bac8c
--- /dev/null
+++ b/tests/ui/structs/method-chain-expression-failure.rs
@@ -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
+}
diff --git a/tests/ui/structs/method-chain-expression-failure.stderr b/tests/ui/structs/method-chain-expression-failure.stderr
new file mode 100644
index 00000000000..ba635ab1fe6
--- /dev/null
+++ b/tests/ui/structs/method-chain-expression-failure.stderr
@@ -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`.
diff --git a/tests/ui/suggestions/chain-method-call-mutation-in-place.stderr b/tests/ui/suggestions/chain-method-call-mutation-in-place.stderr
index 128160f10ad..2dd6fb6a3bf 100644
--- a/tests/ui/suggestions/chain-method-call-mutation-in-place.stderr
+++ b/tests/ui/suggestions/chain-method-call-mutation-in-place.stderr
@@ -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