rust-lang / rust-lang/rust-analyzer
Fails to highlight chained attribute macros that parsed their function
Nobody has claimed this yet.
- 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.
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()
}
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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