Skip to content

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

Closed

Description

@estebank

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

  1. 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
  2. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 25, 2023
  3. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 25, 2023
  4. iSwapna commented on Aug 25, 2023

    @iSwapna
    Contributor

    @rustbot claim

  5. 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?

  6. 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!

  7. 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;
    +                }
    +            }
             }
    :
    
  8. 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?

  9. iSwapna commented on Sep 11, 2023

    @iSwapna
    Contributor

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

  10. added a commit that references this issue on Nov 10, 2023
  11. 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

    Issue actions