Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Extended key-value attributes result in broken macro expansion
cc https://github.com/rust-analyzer/rust-analyzer/issues/8092

```rust
macro_rules! enum_from_u32 {
    (pub enum $name:ident {
        $($(#[$var_attr:meta])* $variant:ident,)*
    }) => {
        pub enum $name {
            $($variant,)*
        }

        impl $name {}
    }
}

macro_rules! language_item_table {
    (
        $( $variant:ident )*
    ) => {
        enum_from_u32! {
            pub enum LangItem {
                $(
                    #[doc = concat!("The `", "bla", "` lang item.")]
                    ///
                    $variant,
                )*
            }
        }
    }
}

language_item_table! {
    Bool
}
```

Results in:

```rust
pub enum$name{}
impl$name{}
```

...which is parsed as a `name!` macro invocation, causing spurious "unresolved macro call" diagnostics

Pull request description:
Handle extended key value attributes in mbe
fixes #8544

bors r+

Patch:
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index cebb8f4008..9bdf0b5fa2 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -76,42 +76,7 @@ pub(crate) mod fragments {
 
     // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
     pub(crate) fn meta_item(p: &mut Parser) {
-        fn is_delimiter(p: &mut Parser) -> bool {
-            matches!(p.current(), T!['{'] | T!['('] | T!['['])
-        }
-
-        if is_delimiter(p) {
-            items::token_tree(p);
-            return;
-        }
-
-        let m = p.start();
-        while !p.at(EOF) {
-            if is_delimiter(p) {
-                items::token_tree(p);
-                break;
-            } else {
-                // https://doc.rust-lang.org/reference/attributes.html
-                // https://doc.rust-lang.org/reference/paths.html#simple-paths
-                // The start of an meta must be a simple path
-                match p.current() {
-                    IDENT | T![super] | T![self] | T![crate] => p.bump_any(),
-                    T![=] => {
-                        p.bump_any();
-                        match p.current() {
-                            c if c.is_literal() => p.bump_any(),
-                            T![true] | T![false] => p.bump_any(),
-                            _ => {}
-                        }
-                        break;
-                    }
-                    _ if p.at(T![::]) => p.bump(T![::]),
-                    _ => break,
-                }
-            }
-        }
-
-        m.complete(p, TOKEN_TREE);
+        attributes::meta(p);
     }
 
     pub(crate) fn item(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 96791ffc2a..124a10eb26 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -14,6 +14,21 @@ pub(super) fn outer_attrs(p: &mut Parser) {
     }
 }
 
+pub(super) fn meta(p: &mut Parser) {
+    paths::use_path(p);
+
+    match p.current() {
+        T![=] => {
+            p.bump(T![=]);
+            if expressions::expr(p).0.is_none() {
+                p.error("expected expression");
+            }
+        }
+        T!['('] | T!['['] | T!['{'] => items::token_tree(p),
+        _ => {}
+    }
+}
+
 fn attr(p: &mut Parser, inner: bool) {
     let attr = p.start();
     assert!(p.at(T![#]));
@@ -25,18 +40,7 @@ fn attr(p: &mut Parser, inner: bool) {
     }
 
     if p.eat(T!['[']) {
-        paths::use_path(p);
-
-        match p.current() {
-            T![=] => {
-                p.bump(T![=]);
-                if expressions::expr(p).0.is_none() {
-                    p.error("expected expression");
-                }
-            }
-            T!['('] | T!['['] | T!['{'] => items::token_tree(p),
-            _ => {}
-        }
+        meta(p);
 
         if !p.eat(T![']']) {
             p.error("expected `]`");


Test patch:
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index e02d038b65..146b236e26 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -940,6 +940,24 @@ fn test_meta_doc_comments() {
     );
 }
 
+#[test]
+fn test_meta_extended_key_value_attributes() {
+    parse_macro(
+        r#"
+macro_rules! foo {
+    (#[$i:meta]) => (
+        #[$ i]
+        fn bar() {}
+    )
+}
+"#,
+    )
+    .assert_expand_items(
+        r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#,
+        r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#,
+    );
+}
+
 #[test]
 fn test_meta_doc_comments_non_latin() {
     parse_macro(