Repository: dtolnay/syn (rust)

Issue:
Parse top level const/static without value
As seen in https://github.com/rust-lang/rust/blob/8aa9d2014f4e5258f83b907e8431c59a33acdae7/src/test/ui/parser/item-free-const-no-body-syntactic-pass.rs and https://github.com/rust-lang/rust/blob/8aa9d2014f4e5258f83b907e8431c59a33acdae7/src/test/ui/parser/item-free-static-no-body-syntactic-pass.rs.

```rust
const X: u8;

static X: u8;
```

Rustc accepts this syntactically but not semantically.

Pull request description:
Parse top level const/static without value
It cannot be parsed as `Item{Const, Struct}` because `eq_token` and `expr` fields are not `Option`, so it parsed as `Item::Verbatim` [like `&raw [const|mut]` syntax](https://github.com/dtolnay/syn/blob/011b9dfc2d04bb1f2aee1b43e2bd88a9a067a568/src/expr.rs#L1495-L1497).

Closes #764

Patch:
diff --git a/src/item.rs b/src/item.rs
index 7b9ccb6d..9d312e76 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -1164,6 +1164,7 @@ pub mod parsing {
 
     impl Parse for Item {
         fn parse(input: ParseStream) -> Result<Self> {
+            let begin = input.fork();
             let mut attrs = input.call(Attribute::parse_outer)?;
             let ahead = input.fork();
             let vis: Visibility = ahead.parse()?;
@@ -1194,12 +1195,61 @@ pub mod parsing {
             } else if lookahead.peek(Token![use]) {
                 input.parse().map(Item::Use)
             } else if lookahead.peek(Token![static]) {
-                input.parse().map(Item::Static)
+                let vis = input.parse()?;
+                let static_token = input.parse()?;
+                let mutability = input.parse()?;
+                let ident = input.parse()?;
+                let colon_token = input.parse()?;
+                let ty = input.parse()?;
+                if input.peek(Token![;]) {
+                    input.parse::<Token![;]>()?;
+                    Ok(Item::Verbatim(verbatim::between(begin, input)))
+                } else {
+                    Ok(Item::Static(ItemStatic {
+                        attrs: Vec::new(),
+                        vis,
+                        static_token,
+                        mutability,
+                        ident,
+                        colon_token,
+                        ty,
+                        eq_token: input.parse()?,
+                        expr: input.parse()?,
+                        semi_token: input.parse()?,
+                    }))
+                }
             } else if lookahead.peek(Token![const]) {
                 ahead.parse::<Token![const]>()?;
                 let lookahead = ahead.lookahead1();
                 if lookahead.peek(Ident) || lookahead.peek(Token![_]) {
-                    input.parse().map(Item::Const)
+                    let vis = input.parse()?;
+                    let const_token = input.parse()?;
+                    let ident = {
+                        let lookahead = input.lookahead1();
+                        if lookahead.peek(Ident) || lookahead.peek(Token![_]) {
+                            input.call(Ident::parse_any)?
+                        } else {
+                            return Err(lookahead.error());
+                        }
+                    };
+                    let colon_token = input.parse()?;
+                    let ty = input.parse()?;
+                    if input.peek(Token![;]) {
+                        input.parse::<Token![;]>()?;
+                        Ok(Item::Verbatim(verbatim::between(begin, input)))
+                    } else {
+                        Ok(Item::Const(ItemConst {
+                            attrs: Vec::new(),
+                            vis,
+                            const_token,
+                            ident,
+                            colon_token,
+                            ty,
+                            eq_token: input.parse()?,
+                            expr: input.parse()?,
+                            semi_token: input.parse()?,
+                        }))
+                    }
                 } else if lookahead.peek(Token![unsafe])
                     || lookahead.peek(Token![async])
                     || lookahead.peek(Token![extern])


Test patch:
diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs
index 05ca7a60..3b125ee0 100644
--- a/tests/repo/mod.rs
+++ b/tests/repo/mod.rs
@@ -72,11 +72,6 @@ pub fn base_dir_filter(entry: &DirEntry) -> bool {
         // https://github.com/dtolnay/syn/issues/762
         "test/ui/parser/foreign-static-syntactic-pass.rs" |
 
-        // TODO: top level const/static without value: `const X: u8;`
-        // https://github.com/dtolnay/syn/issues/764
-        "test/ui/parser/item-free-const-no-body-syntactic-pass.rs" |
-        "test/ui/parser/item-free-static-no-body-syntactic-pass.rs" |
-
         // TODO: mut receiver in fn pointer type: `fn(mut self)`
         // https://github.com/dtolnay/syn/issues/765
         "test/ui/parser/self-param-syntactic-pass.rs" |