rust-lang / rust-lang/rust-analyzer

Fails to highlight chained attribute macros that parsed their function

Open
#20,593 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-proc-macro C-bug
Dominant language
Rust
Stars
16.9k
Forks
2.2k
Avg merge
1d 12h
Merged PRs (30d)
72

Description

rust-analyzer version: 0.3.2593-standalone
rustc version: 1.89.0
editor or extension: VScode (rust-analyzer extension)

dependencies

proc-macro2 = "1.0.101"
quote = "1.0.40"
syn = { version = "2.0.106", features = ["full"] }

Steps to Reproduce

If a function is given multiple attribute macros that parse the provided token stream as a syn::Item or a syn::ItemFn rust-analyzer may not properly highlight the attributes that come after the first one that does the parsing. Take the following two proc macros. They currently don't do anything -- they simply parse the the function so we can change it if needed and then emit that function as a token stream.

use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;

/// Tag1 macro.
#[proc_macro_attribute]
pub fn tag1(_attr: TokenStream, function: TokenStream) -> TokenStream {
    let f = parse_macro_input!(function as syn::ItemFn);

    /* modify function as needed based on attr data */

    quote! { #f }.into()
}

/// Tag2 macro.
#[proc_macro_attribute]
pub fn tag2(_attr: TokenStream, function: TokenStream) -> TokenStream {
    let f = parse_macro_input!(function as syn::ItemFn);

    /* modify function as needed based on attr data */

    quote! { #f }.into()
}

However, as you can see in the screenshot below, tag2 is not highlighted and cannot be navigated to, nor can its rustdoc been seen on hover.

Image

This does not occur when emitting the function out without parsing. We can still quote it if we want. Note below that we just take the token stream and convert it into a proc_macro2::TokenStream and emit it back out:

/// Tag1 macro.
#[proc_macro_attribute]
pub fn tag1(_attr: TokenStream, function: TokenStream) -> TokenStream {
    let f: proc_macro2::TokenStream = function.into();

    /* modify function as needed based on attr data */

    quote! { #f }.into()
}

/// Tag2 macro.
#[proc_macro_attribute]
pub fn tag2(_attr: TokenStream, function: TokenStream) -> TokenStream {
    let f: proc_macro2::TokenStream = function.into();

    /* modify function as needed based on attr data */

    quote! { #f }.into()
}
Image

Please note that in both cases the proc macros compiled successfully and performed as expected. It's just that in the scenario that tokens are parsed, rust-analyzer seems to forget about these attributes in this very specific case. If the failure here is due to syn rather than rust-analyzer, please let me know and I'll file a report to syn regarding this behavior.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the two chained proc_macro_attribute examples using syn::ItemFn and parse_macro_input!, then compare them with the TokenStream-only variant. Investigate rust-analyzer's handling of parsed attribute macro output. Done means later attributes remain highlighted, navigable, and available for rustdoc hover.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.