Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
estebank opened this issue Aug 25, 2023 · 7 comments
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-papercut Diagnostics: An error or lint that needs small tweaks. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Aug 25, 2023

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

@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. D-papercut Diagnostics: An error or lint that needs small tweaks. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Aug 25, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Aug 25, 2023
@compiler-errors compiler-errors removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Aug 25, 2023
@iSwapna
Copy link
Contributor

iSwapna commented Aug 25, 2023

@rustbot claim

@iSwapna
Copy link
Contributor

iSwapna commented Aug 28, 2023

@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
Copy link
Contributor Author

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
Copy link
Contributor

iSwapna commented Sep 5, 2023

@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
Copy link
Contributor

iSwapna commented Sep 5, 2023

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
Copy link
Contributor

iSwapna commented Sep 11, 2023

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

bors added a commit to rust-lang-ci/rust that referenced this issue Nov 10, 2023
On method chain expression failure, look for missing method in earlier segments of the chain

This PR tries to fix the issue: rust-lang#115222

As suggested by `@estebank` , I did the following:
1. Add new test `tests/ui/structs/method-chain-expression-failure.rs`
2. In `compiler/rusct_hir_tycheck/src/method/suggest.rs`
   walking up the method chain and calling `probe_for_name` with the method name. But the call fails to return `Ok`.
@Dylan-DPC
Copy link
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
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-papercut Diagnostics: An error or lint that needs small tweaks. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants