Skip to content

On method chain expression failure, look for missing method in earlier segments of the chain #115222

Closed
@estebank

Description

@estebank
Contributor

Code

struct A;
struct B;
struct C;
struct D;

impl A {
    fn b(&self) -> B { B }
}
impl B {
    fn c(&self) -> C { C }
    fn foo(&self) {}
}
impl C {
    fn d(&self) -> D { D }
}
impl D {
    fn e(&self) {}
}

fn main() {
    A.b().c().d().foo();
}

Current output

error[E0599]: no method named `foo` found for struct `D` in the current scope
  --> src/main.rs:21:19
   |
4  | struct D;
   | -------- method `foo` not found for this struct
...
21 |     A.b().c().d().foo();
   |                   ^^^ method not found in `D`

Desired output

error[E0599]: no method named `foo` found for struct `D` in the current scope
  --> src/main.rs:21:19
   |
2  | struct B;
   | -------- method `foo` found for this struct
3  | struct C;
4  | struct D;
   | -------- method `foo` not found for this struct
...
21 |     A.b().c().d().foo();
   |     -----         ^^^ method not found in `D`
   |     |
   |     method `foo` available after this method call

Rationale and extra context

No response

Other cases

No response

Anything else?

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=5d27c80570db4a4005f5ff3d4ea94e0e

Activity

added
A-diagnosticsArea: Messages for errors, warnings, and lints
P-lowLow priority
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
D-papercutDiagnostics: An error or lint that needs small tweaks.
D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.
on Aug 25, 2023
added
needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.
on Aug 25, 2023
removed
needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.
on Aug 25, 2023
iSwapna

iSwapna commented on Aug 25, 2023

@iSwapna
Contributor

@rustbot claim

iSwapna

iSwapna commented on Aug 28, 2023

@iSwapna
Contributor

@estebank Hi, I am new to Rust and need some help landing this fix. Would you be the right person to ask questions?

What I have found out so far is that the Diagnostic is generated from here: compiler/rustc_hir_typeck/src/method/suggest.rs:385:22. I think we need to find where in the struct inheritance chain the method appears while walking up the chain from current struct and write out a diagnostic from there. Is that the right way ? If so, is there a compiler pass that will help me walk up the struct chain?

estebank

estebank commented on Aug 28, 2023

@estebank
ContributorAuthor

Hi @iSwapna! There are two things at play here: typeck which you just found and the HIR (high level intermediate representation) which holds the "hydrated AST", a fancy way of saying "every component bit of code with syntax desugaring applied and access to their type information so far". So what you want to do is look at 468 if let SelfSource::MethodCall(rcvr_expr) = source { and use that rcvr_expr to go up the method chain (the receiver is foo in foo.bar(), if you have foo.bar().baz() you'll have a MethodCall(receiver: foo.bar(), path_segment: baz, args: [])). You can look at

for an example of us doing exactly this. The main difference will be that instead of creating new "obligations" and calling can_eq we'll be looking at theexpr_ty_adjusted_opt for each receiver and then call probe_for_name with the method name that failed on each of those receivers (like we do in note_derefed_ty_has_method). Do not hesitate to ask more questions!

iSwapna

iSwapna commented on Sep 5, 2023

@iSwapna
Contributor

@estebank Here is what I have so far:

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 72a04a02bf4..943ba9495b6 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -485,6 +485,41 @@ pub fn report_no_match_method_error(
                 expected.to_option(&self),
                 rcvr_ty,
             );
+
+            let mut rcvr_expr = rcvr_expr;
+            while let hir::ExprKind::MethodCall(_path_segment, parent_expr, _args, span) =
+            rcvr_expr.kind
+            {
+                // Point at every method call in the chain with the resulting type.
+                // vec![1, 2, 3].iter().map(mapper).sum<i32>()
+                //
+                rcvr_expr = parent_expr;
+                let prev_ty = self.resolve_vars_if_possible(
+                    self.typeck_results
+                        .borrow()
+                        .expr_ty_adjusted_opt(rcvr_expr)
+                        .unwrap_or(Ty::new_misc_error(self.tcx)),
+                );
+                err.span_note(
+                    span,
+                    format!("TEST: the method `{item_name}` is implemented on `{prev_ty}`"),
+                );
+                if let Ok(_pick) = self.probe_for_name(
+                    Mode::Path,
+                    item_name,
+                    expected.only_has_type(self),
+                    IsSuggestion(true),
+                    prev_ty,
+                    rcvr_expr.hir_id,
+                    ProbeScope::AllTraits,
+                ) {
+                    err.span_note(
+                        span,
+                        format!("the method `{item_name}` is implemented on `{prev_ty}`"),
+                    );
+                    break;
+                }
+            }
         }
:
iSwapna

iSwapna commented on Sep 5, 2023

@iSwapna
Contributor

I am playing with err.span_note in the first instance to understand how to adjust it to generate the correct type. But looks like I need to use a different level of diagnostic message to display the first part of expected output:

 | -------- method `foo` found for this struct

How do I do that?

iSwapna

iSwapna commented on Sep 11, 2023

@iSwapna
Contributor

Opened a PR as suggested by @oli-obk here: #115229

added a commit that references this issue on Nov 10, 2023

Auto merge of rust-lang#115229 - iSwapna:issue-115222-fix, r=estebank

Dylan-DPC

Dylan-DPC commented on Sep 13, 2024

@Dylan-DPC
Member

Current output:

   |
4  | struct D;
   | -------- method `foo` not found for this struct
...
21 |     A.b().c().d().foo();
   |       ---         ^^^ method not found in `D`
   |       |
   |       method `foo` is available on `&B`

So this should be good enough to consider as resolved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-diagnosticsArea: Messages for errors, warnings, and lintsD-papercutDiagnostics: An error or lint that needs small tweaks.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.P-lowLow priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @estebank@compiler-errors@rustbot@iSwapna@Dylan-DPC

      Issue actions

        On method chain expression failure, look for missing method in earlier segments of the chain · Issue #115222 · rust-lang/rust