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

Fix incorrect diagnostics for failing built in macros #7970

Merged
merged 3 commits into from Mar 15, 2021

Conversation

brandondong
Copy link
Contributor

@brandondong brandondong commented Mar 11, 2021

Reproduction:

  1. Use a built in macro in such a way that rust-analyzer fails to expand it. For example:

lib.rs

include!("<valid file but without a .rs extension so it is not indexed by rust-analyzer>");
  1. rust-analyzer highlights the macro call and says the macro itself cannot be resolved even though include! is in the standard library (unresolved-macro-call diagnostic).
  2. No macro-error diagnostic is raised.

Root cause for incorrect unresolved-macro-call diagnostic:

  1. collector:collect_macro_call is able to resolve include! in legacy scope but the expansion fails. Therefore, it's pushed into unexpanded_macros to be retried with module scope.
  2. include! fails at the resolution step in collector:resolve_macros now that it's using module scope. Therefore, it's retained in unexpanded_macros.
  3. Finally, collector:finish tries resolving the remaining unexpanded macros but only with module scope. include! again fails at the resolution step so a diagnostic is created.

Root cause for missing macro-error diagnostic:

  1. In collector:resolve_macros, directive.legacy is None since eager expansion failed in collector:collect_macro_call. The macro_call_as_call_id fails to resolve since we're retrying in module scope. Therefore, collect_macro_expansion is not called for the macro and no macro-error diagnostic is generated.

Fix:

  • In collector:collect_macro_call, do not add failing built-in macros to the unexpanded_macros list and immediately raise the macro-error diagnostic. This is in contrast to lazy macros which are resolved in collector::resolve_macros and later expanded in collect_macro_expansion where a macro-error diagnostic may be raised.

@jonas-schievink
Copy link
Contributor

Thanks, this looks good! Can you add a test to crates/hir_def/src/nameres/tests/diagnostics.rs? Something like this should work:

    check_diagnostics(
        r#"
        #[rustc_builtin_macro]
        macro_rules! include { () => {} }

        include!("doesntexist");
      //^^^^^^^^^^^^^^^^^^^^^^^ <text of diagnostic>
        "#,
    );

@brandondong
Copy link
Contributor Author

Just to clarify, the behavior before my changes is that an incorrect unresolved-macro-call diagnostic would be raised in this scenario. No macro-error diagnostic on the other hand would be raised.

This is because in collector:resolve_macros, directive.legacy is None since eager expansion failed in collector:collect_macro_call. The macro_call_as_call_id fails to resolve since we're retrying in module scope. Therefore, collect_macro_expansion is not called for the macro and no macro-error diagnostic is generated. I believe that even if the code did make it there successfully, it would stop in collector:collect_macro_expansion because of the FIXME to handle eager macros.

My change only removes the incorrect unresolved-macro-call diagnostic. I can create a test to assert no diagnostics if that is desired.

I am assuming the optimal behavior of the system would be something like:

  • macro-error is true if any of legacy or module resolutions (or both) succeeded but no expansions succeeded.
  • unresolved-macro-call is true if both legacy and module resolutions failed (what this change implements).

I could try implementing the first bullet point as well but it appears to be a much more involved change.

@jonas-schievink
Copy link
Contributor

Hmm, I see. I'm not entirely sure what the best fix here is – it's a bit complicated with eager expansion happening effectively during macro resolution.

@brandondong brandondong changed the title Fix incorrect unresolved-macro-call diagnostic when built in macro fails Fix incorrect diagnostics for failing built in macros Mar 14, 2021
@brandondong
Copy link
Contributor Author

I took a look at the missing macro-error diagnostic issue. I believe I have a better understanding of the code now and redid the fix. If you could take another look, that would be greatly appreciated.

Copy link
Contributor

@jonas-schievink jonas-schievink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks! Left one minor nit.

crates/hir_def/src/nameres/collector.rs Outdated Show resolved Hide resolved
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
@jonas-schievink
Copy link
Contributor

bors r+

@bors
Copy link
Contributor

bors bot commented Mar 15, 2021

@bors bors bot merged commit 47b74ca into rust-lang:master Mar 15, 2021
@brandondong brandondong deleted the unresolved_macro_call_error branch March 16, 2021 04:16
bors bot added a commit that referenced this pull request Mar 17, 2021
8048: Fix missing unresolved macro diagnostic in function body r=edwin0cheng a=brandondong

This was an issue I found while working on #7970.

**Reproduction:**
1. Call a non-existent macro in a function body.
```
fn main() {
  foo!();
}
```
2. No diagnostics are raised. An unresolved-macro-call diagnostic is expected.
3. If the macro call is instead outside of the function body, this works as expected.

I believe this worked previously and regressed in #7805.

**Behavior prior to #7805
- The unresolved-macro-call diagnostic did not exist. Instead, a macro-error diagnostic would be raised with the text "could not resolve macro [path]".
- This was implemented by adding an error to the error sink (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8L657).
- The error was propagated through https://github.com/rust-analyzer/rust-analyzer/blob/1a82af3527e476d52410ff4dfd2fb4c57466abcb/crates/hir_def/src/body.rs#L123 eventually reaching https://github.com/rust-analyzer/rust-analyzer/blob/1a82af3527e476d52410ff4dfd2fb4c57466abcb/crates/hir_def/src/body/lower.rs#L569.

**Behavior after:**
- Instead of writing to the error sink, an UnresolvedMacro error is now returned (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8R631).
- The parent caller throws away the error as its function signature is `Option<MacroCallId>` (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8R604).
- We instead now reach the warn condition (https://github.com/rust-analyzer/rust-analyzer/blob/1a82af3527e476d52410ff4dfd2fb4c57466abcb/crates/hir_def/src/body.rs#L124) and no diagnostics are created in https://github.com/rust-analyzer/rust-analyzer/blob/1a82af3527e476d52410ff4dfd2fb4c57466abcb/crates/hir_def/src/body/lower.rs#L575.

**Fix:**
- Make sure to propagate the UnresolvedMacro error. Report the error using the new unresolved-macro-call diagnostic.


Co-authored-by: Brandon <brandondong604@hotmail.com>
@lnicola
Copy link
Member

lnicola commented Mar 20, 2021

changelog fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants