Repository: dtolnay/syn (rust)

Issue:
Parse extern static with value
As seen in https://github.com/rust-lang/rust/blob/8aa9d2014f4e5258f83b907e8431c59a33acdae7/src/test/ui/parser/foreign-static-syntactic-pass.rs.

```rust
extern {
    static X: u8 = 0;
}
```

Rustc accepts this syntactically but not semantically.

Pull request:
Parse extern static with value
It cannot be parsed as `ForeignItemStatic` , so it parsed as `ForeignItem::Verbatim` like `&raw [const|mut]` syntax.

Closes #762

Patch:
diff --git a/src/expr.rs b/src/expr.rs
index 88fea215..3c9cb1b3 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -2009,7 +2009,32 @@ pub(crate) mod parsing {
         Ok(ExprLet {
             attrs: Vec::new(),
             let_token: input.parse()?,
-            pat: pat::parsing::multi_pat_with_leading_vert(input)?,
+            pat: {
+                let leading_vert: Option<Token![|]> = input.parse()?;
+                let pat: Pat = input.parse()?;
+                if leading_vert.is_some()
+                    || input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=])
+                {
+                    let mut cases = Punctuated::new();
+                    cases.push_value(pat);
+                    while input.peek(Token![|])
+                        && !input.peek(Token![||])
+                        && !input.peek(Token![|=])
+                    {
+                        let punct = input.parse()?;
+                        cases.push_punct(punct);
+                        let pat: Pat = input.parse()?;
+                        cases.push_value(pat);
+                    }
+                    Pat::Or(PatOr {
+                        attrs: Vec::new(),
+                        leading_vert,
+                        cases,
+                    })
+                } else {
+                    pat
+                }
+            },
             eq_token: input.parse()?,
             expr: Box::new(input.call(expr_no_struct)?),
         })
@@ -2018,9 +2043,8 @@ pub(crate) mod parsing {
     #[cfg(feature = "full")]
     impl Parse for ExprIf {
         fn parse(input: ParseStream) -> Result<Self> {
-            let attrs = input.call(Attribute::parse_outer)?;
             Ok(ExprIf {
-                attrs,
+                attrs: Vec::new(),
                 if_token: input.parse()?,
                 cond: Box::new(input.call(expr_no_struct)?),
                 then_branch: input.parse()?,
@@ -2058,11 +2082,26 @@ pub(crate) mod parsing {
     #[cfg(feature = "full")]
     impl Parse for ExprForLoop {
         fn parse(input: ParseStream) -> Result<Self> {
-            let outer_attrs = input.call(Attribute::parse_outer)?;
             let label: Option<Label> = input.parse()?;
             let for_token: Token![for] = input.parse()?;
 
-            let pat = pat::parsing::multi_pat_with_leading_vert(input)?;
+            let leading_vert: Option<Token![|]> = input.parse()?;
+            let mut pat: Pat = input.parse()?;
+            if leading_vert.is_some() || input.peek(Token![|]) {
+                let mut cases = Punctuated::new();
+                cases.push_value(pat);
+                while input.peek(Token![|]) {
+                    let punct = input.parse()?;
+                    cases.push_punct(punct);
+                    let pat: Pat = input.parse()?;
+                    cases.push_value(pat);
+                }
+                pat = Pat::Or(PatOr {
+                    attrs: Vec::new(),
+                    leading_vert,
+                    cases,
+                });
+            }
 
             let in_token: Token![in] = input.parse()?;
             let expr: Expr = input.call(expr_no_struct)?;
@@ -2073,7 +2112,7 @@ pub(crate) mod parsing {
             let stmts = content.call(Block::parse_within)?;
 
             Ok(ExprForLoop {
-                attrs: private::attrs(outer_attrs, inner_attrs),
+                attrs: inner_attrs,
                 label,
                 for_token,
                 pat,
@@ -2087,7 +2126,6 @@ pub(crate) mod parsing {
     #[cfg(feature = "full")]
     impl Parse for ExprLoop {
         fn parse(input: ParseStream) -> Result<Self> {
-            let outer_attrs = input.call(Attribute::parse_outer)?;
             let label: Option<Label> = input.parse()?;
             let loop_token: Token![loop] = input.parse()?;
 
@@ -2097,7 +2135,7 @@ pub(crate) mod parsing {
             let stmts = content.call(Block::parse_within)?;
 
             Ok(ExprLoop {
-                attrs: private::attrs(outer_attrs, inner_attrs),
+                attrs: inner_attrs,
                 label,
                 loop_token,
                 body: Block { brace_token, stmts },
@@ -2108,7 +2146,6 @@ pub(crate) mod parsing {
     #[cfg(feature = "full")]
     impl Parse for ExprMatch {
         fn parse(input: ParseStream) -> Result<Self> {
-            let outer_attrs = input.call(Attribute::parse_outer)?;
             let match_token: Token![match] = input.parse()?;
             let expr = expr_no_struct(input)?;
 
@@ -2122,7 +2159,7 @@ pub(crate) mod parsing {
             }
 
             Ok(ExprMatch {
-                attrs: private::attrs(outer_attrs, inner_attrs),
+                attrs: inner_attrs,
                 match_token,
                 expr: Box::new(expr),
                 brace_token,
@@ -2317,7 +2354,6 @@ pub(crate) mod parsing {
     #[cfg(feature = "full")]
     impl Parse for ExprWhile {
         fn parse(input: ParseStream) -> Result<Self> {
-            let outer_attrs = input.call(Attribute::parse_outer)?;
             let label: Option<Label> = input.parse()?;
             let while_token: Token![while] = input.parse()?;
             let cond = expr_no_struct(input)?;
@@ -2328,7 +2364,7 @@ pub(crate) mod parsing {
             let stmts = content.call(Block::parse_within)?;
 
             Ok(ExprWhile {
-                attrs: private::attrs(outer_attrs, inner_attrs),
+                attrs: inner_attrs,
                 label,
                 while_token,
                 cond: Box::new(cond),
@@ -2581,7 +2617,27 @@ pub(crate) mod parsing {
             let requires_comma;
             Ok(Arm {
                 attrs: input.call(Attribute::parse_outer)?,
-                pat: pat::parsing::multi_pat_with_leading_vert(input)?,
+                pat: {
+                    let leading_vert: Option<Token![|]> = input.parse()?;
+                    let pat: Pat = input.parse()?;
+                    if leading_vert.is_some() || input.peek(Token![|]) {
+                        let mut cases = Punctuated::new();
+                        cases.push_value(pat);
+                        while input.peek(Token![|]) {
+                            let punct = input.parse()?;
+                            cases.push_punct(punct);
+                            let pat: Pat = input.parse()?;
+                            cases.push_value(pat);
+                        }
+                        Pat::Or(PatOr {
+                            attrs: Vec::new(),
+                            leading_vert,
+                            cases,
+                        })
+                    } else {
+                        pat
+                    }
+                },
                 guard: {
                     if input.peek(Token![if]) {
                         let if_token: Token![if] = input.parse()?;
diff --git a/src/item.rs b/src/item.rs
index 06df7677..be8e561f 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -1164,7 +1164,6 @@ 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()?;
@@ -1195,61 +1194,12 @@ pub mod parsing {
             } else if lookahead.peek(Token![use]) {
                 input.parse().map(Item::Use)
             } else if lookahead.peek(Token![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()?,
-                    }))
-                }
+                input.parse().map(Item::Static)
             } else if lookahead.peek(Token![const]) {
                 ahead.parse::<Token![const]>()?;
                 let lookahead = ahead.lookahead1();
                 if lookahead.peek(Ident) || lookahead.peek(Token![_]) {
-                    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()?,
-                        }))
-                    }
+                    input.parse().map(Item::Const)
                 } else if lookahead.peek(Token![unsafe])
                     || lookahead.peek(Token![async])
                     || lookahead.peek(Token![extern])
@@ -1721,7 +1671,7 @@ pub mod parsing {
 
         Ok(PatType {
             attrs: Vec::new(),
-            pat: Box::new(pat::parsing::multi_pat(input)?),
+            pat: input.parse()?,
             colon_token: input.parse()?,
             ty: Box::new(match input.parse::<Option<Token![...]>>()? {
                 Some(dot3) => Type::Verbatim(variadic_to_tokens(&dot3)),
@@ -1804,7 +1754,31 @@ pub mod parsing {
             let mut item = if lookahead.peek(Token![fn]) {
                 input.parse().map(ForeignItem::Fn)
             } else if lookahead.peek(Token![static]) {
-                input.parse().map(ForeignItem::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![=]>()?;
+                    input.parse::<Expr>()?;
+                    input.parse::<Token![;]>()?;
+
+                    Ok(ForeignItem::Verbatim(verbatim::between(begin, input)))
+                } else {
+                    Ok(ForeignItem::Static(ForeignItemStatic {
+                        attrs: Vec::new(),
+                        vis,
+                        static_token,
+                        mutability,
+                        ident,
+                        colon_token,
+                        ty,
+                        semi_token: input.parse()?,
+                    }))
+                }
             } else if lookahead.peek(Token![type]) {
                 parse_flexible_foreign_item_type(begin, input)
             } else if vis.is_inherited()
diff --git a/src/pat.rs b/src/pat.rs
index b1128ff7..13a67304 100644
--- a/src/pat.rs
+++ b/src/pat.rs
@@ -385,7 +385,7 @@ impl Hash for Pat {
 }
 
 #[cfg(feature = "parsing")]
-pub mod parsing {
+mod parsing {
     use super::*;
 
     use crate::ext::IdentExt;
@@ -596,7 +596,7 @@ pub mod parsing {
                 attrs,
                 member,
                 colon_token: input.parse()?,
-                pat: Box::new(multi_pat(input)?),
+                pat: input.parse()?,
             });
         }
 
@@ -675,7 +675,7 @@ pub mod parsing {
 
         let mut elems = Punctuated::new();
         while !content.is_empty() {
-            let value = multi_pat(&content)?;
+            let value: Pat = content.parse()?;
             elems.push_value(value);
             if content.is_empty() {
                 break;
@@ -771,7 +771,7 @@ pub mod parsing {
 
         let mut elems = Punctuated::new();
         while !content.is_empty() {
-            let value = multi_pat(&content)?;
+            let value: Pat = content.parse()?;
             elems.push_value(value);
             if content.is_empty() {
                 break;
@@ -786,37 +786,6 @@ pub mod parsing {
             elems,
         })
     }
-
-    pub fn multi_pat(input: ParseStream) -> Result<Pat> {
-        multi_pat_impl(input, None)
-    }
-
-    pub fn multi_pat_with_leading_vert(input: ParseStream) -> Result<Pat> {
-        let leading_vert: Option<Token![|]> = input.parse()?;
-        multi_pat_impl(input, leading_vert)
-    }
-
-    fn multi_pat_impl(input: ParseStream, leading_vert: Option<Token![|]>) -> Result<Pat> {
-        let mut pat: Pat = input.parse()?;
-        if leading_vert.is_some()
-            || input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=])
-        {
-            let mut cases = Punctuated::new();
-            cases.push_value(pat);
-            while input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) {
-                let punct = input.parse()?;
-                cases.push_punct(punct);
-                let pat: Pat = input.parse()?;
-                cases.push_value(pat);
-            }
-            pat = Pat::Or(PatOr {
-                attrs: Vec::new(),
-                leading_vert,
-                cases,
-            });
-        }
-        Ok(pat)
-    }
 }
 
 #[cfg(feature = "printing")]
diff --git a/src/stmt.rs b/src/stmt.rs
index a3765d58..9706b931 100644
--- a/src/stmt.rs
+++ b/src/stmt.rs
@@ -49,6 +49,7 @@ pub mod parsing {
 
     use crate::parse::discouraged::Speculative;
     use crate::parse::{Parse, ParseStream, Result};
+    use crate::punctuated::Punctuated;
     use proc_macro2::TokenStream;
 
     impl Block {
@@ -219,7 +220,28 @@ pub mod parsing {
             attrs,
             let_token: input.parse()?,
             pat: {
-                let mut pat: Pat = pat::parsing::multi_pat_with_leading_vert(input)?;
+                let leading_vert: Option<Token![|]> = input.parse()?;
+                let mut pat: Pat = input.parse()?;
+                if leading_vert.is_some()
+                    || input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=])
+                {
+                    let mut cases = Punctuated::new();
+                    cases.push_value(pat);
+                    while input.peek(Token![|])
+                        && !input.peek(Token![||])
+                        && !input.peek(Token![|=])
+                    {
+                        let punct = input.parse()?;
+                        cases.push_punct(punct);
+                        let pat: Pat = input.parse()?;
+                        cases.push_value(pat);
+                    }
+                    pat = Pat::Or(PatOr {
+                        attrs: Vec::new(),
+                        leading_vert,
+                        cases,
+                    });
+                }
                 if input.peek(Token![:]) {
                     let colon_token: Token![:] = input.parse()?;
                     let ty: Type = input.parse()?;


Test patch:
diff --git a/tests/common/eq.rs b/tests/common/eq.rs
index 7025449a..1fc1b5ce 100644
--- a/tests/common/eq.rs
+++ b/tests/common/eq.rs
@@ -11,8 +11,7 @@ use rustc_ast::ast::{
     BinOpKind, BindingMode, Block, BlockCheckMode, BorrowKind, CaptureBy, Const, Crate, CrateSugar,
     Defaultness, EnumDef, Expr, ExprKind, Extern, Field, FieldPat, FloatTy, FnDecl, FnHeader,
     FnRetTy, FnSig, ForeignItemKind, ForeignMod, GenericArg, GenericArgs, GenericBound,
-    GenericParam, GenericParamKind, Generics, GlobalAsm, ImplPolarity, InlineAsm, InlineAsmOperand,
-    InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, IntTy, IsAuto, Item,
+    GenericParam, GenericParamKind, Generics, GlobalAsm, ImplPolarity, IntTy, IsAuto, Item,
     ItemKind, Label, Lifetime, Lit, LitFloatType, LitIntType, LitKind, LlvmAsmDialect,
     LlvmInlineAsm, LlvmInlineAsmOutput, Local, MacArgs, MacCall, MacDelimiter, MacStmtStyle,
     MacroDef, Mod, Movability, MutTy, Mutability, NodeId, Param, ParenthesizedArgs, Pat, PatKind,
@@ -125,10 +124,8 @@ spanless_eq_partial_eq!(u16);
 spanless_eq_partial_eq!(u128);
 spanless_eq_partial_eq!(usize);
 spanless_eq_partial_eq!(char);
-spanless_eq_partial_eq!(String);
 spanless_eq_partial_eq!(Symbol);
 spanless_eq_partial_eq!(DelimToken);
-spanless_eq_partial_eq!(InlineAsmOptions);
 
 macro_rules! spanless_eq_struct {
     {
@@ -283,7 +280,6 @@ spanless_eq_struct!(ForeignMod; abi items);
 spanless_eq_struct!(GenericParam; id ident attrs bounds is_placeholder kind);
 spanless_eq_struct!(Generics; params where_clause span);
 spanless_eq_struct!(GlobalAsm; asm);
-spanless_eq_struct!(InlineAsm; template operands options);
 spanless_eq_struct!(Item<K>; attrs id span vis ident kind !tokens);
 spanless_eq_struct!(Label; ident);
 spanless_eq_struct!(Lifetime; id ident);
@@ -337,8 +333,6 @@ spanless_eq_enum!(GenericArgs; AngleBracketed(0) Parenthesized(0));
 spanless_eq_enum!(GenericBound; Trait(0 1) Outlives(0));
 spanless_eq_enum!(GenericParamKind; Lifetime Type(default) Const(ty));
 spanless_eq_enum!(ImplPolarity; Positive Negative(0));
-spanless_eq_enum!(InlineAsmRegOrRegClass; Reg(0) RegClass(0));
-spanless_eq_enum!(InlineAsmTemplatePiece; String(0) Placeholder(operand_idx modifier span));
 spanless_eq_enum!(IntTy; Isize I8 I16 I32 I64 I128);
 spanless_eq_enum!(IsAuto; Yes No);
 spanless_eq_enum!(LitFloatType; Suffixed(0) Unsuffixed);
@@ -369,11 +363,8 @@ spanless_eq_enum!(ExprKind; Box(0) Array(0) Call(0 1) MethodCall(0 1) Tup(0)
     While(0 1 2) ForLoop(0 1 2 3) Loop(0 1) Match(0 1) Closure(0 1 2 3 4 5)
     Block(0 1) Async(0 1 2) Await(0) TryBlock(0) Assign(0 1 2) AssignOp(0 1 2)
     Field(0 1) Index(0 1) Range(0 1 2) Path(0 1) AddrOf(0 1 2) Break(0 1)
-    Continue(0) Ret(0) InlineAsm(0) LlvmInlineAsm(0) MacCall(0) Struct(0 1 2)
-    Repeat(0 1) Paren(0) Try(0) Yield(0) Err);
-spanless_eq_enum!(InlineAsmOperand; In(reg expr) Out(reg late expr)
-    InOut(reg late expr) SplitInOut(reg late in_expr out_expr) Const(expr)
-    Sym(expr));
+    Continue(0) Ret(0) LlvmInlineAsm(0) MacCall(0) Struct(0 1 2) Repeat(0 1)
+    Paren(0) Try(0) Yield(0) Err);
 spanless_eq_enum!(ItemKind; ExternCrate(0) Use(0) Static(0 1 2) Const(0 1 2)
     Fn(0 1 2 3) Mod(0) ForeignMod(0) GlobalAsm(0) TyAlias(0 1 2 3) Enum(0 1)
     Struct(0 1) Union(0 1) Trait(0 1 2 3 4) TraitAlias(0 1)
diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs
index 2eb24dfd..05d31dbb 100644
--- a/tests/repo/mod.rs
+++ b/tests/repo/mod.rs
@@ -43,14 +43,35 @@ pub fn base_dir_filter(entry: &DirEntry) -> bool {
     }
 
     match path {
+        // TODO: or-patterns patterns: `Some(1 | 8)`
+        // https://github.com/dtolnay/syn/issues/758
+        "test/mir-opt/exponential-or.rs" |
+        "test/ui/or-patterns/basic-switch.rs" |
+        "test/ui/or-patterns/basic-switchint.rs" |
+        "test/ui/or-patterns/bindings-runpass-1.rs" |
+        "test/ui/or-patterns/bindings-runpass-2.rs" |
+        "test/ui/or-patterns/consistent-bindings.rs" |
+        "test/ui/or-patterns/exhaustiveness-pass.rs" |
+        "test/ui/or-patterns/for-loop.rs" |
+        "test/ui/or-patterns/if-let-while-let.rs" |
+        "test/ui/or-patterns/issue-67514-irrefutable-param.rs" |
+        "test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs" |
+        "test/ui/or-patterns/let-pattern.rs" |
+        "test/ui/or-patterns/mix-with-wild.rs" |
+        "test/ui/or-patterns/or-patterns-default-binding-modes.rs" |
+        "test/ui/or-patterns/or-patterns-syntactic-pass.rs" |
+        "test/ui/or-patterns/search-via-bindings.rs" |
+        "test/ui/or-patterns/struct-like.rs" |
+
         // TODO: top level fn without body: `fn f();`
         // https://github.com/dtolnay/syn/issues/761
         "test/ui/parser/fn-body-optional-syntactic-pass.rs" |
         "test/ui/parser/fn-header-syntactic-pass.rs" |
 
-        // TODO: extern static with value: `extern { static X: u8 = 0; }`
-        // 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
diff --git a/tests/test_pat.rs b/tests/test_pat.rs
index 73388dd7..8cba735f 100644
--- a/tests/test_pat.rs
+++ b/tests/test_pat.rs
@@ -1,5 +1,5 @@
 use quote::quote;
-use syn::{Item, Pat, Stmt};
+use syn::Pat;
 
 #[test]
 fn test_pat_ident() {
@@ -16,23 +16,3 @@ fn test_pat_path() {
         value => panic!("expected PatPath, got {:?}", value),
     }
 }
-
-#[test]
-fn test_leading_vert() {
-    // https://github.com/rust-lang/rust/blob/1.43.0/src/test/ui/or-patterns/remove-leading-vert.rs
-
-    syn::parse_str::<Item>("fn f() {}").unwrap();
-    syn::parse_str::<Item>("fn fun1(| A: E) {}").unwrap_err();
-    syn::parse_str::<Item>("fn fun2(|| A: E) {}").unwrap_err();
-
-    syn::parse_str::<Stmt>("let | () = ();").unwrap();
-    syn::parse_str::<Stmt>("let (| A): E;").unwrap_err();
-    syn::parse_str::<Stmt>("let (|| A): (E);").unwrap_err();
-    syn::parse_str::<Stmt>("let (| A,): (E,);").unwrap_err();
-    syn::parse_str::<Stmt>("let [| A]: [E; 1];").unwrap_err();
-    syn::parse_str::<Stmt>("let [|| A]: [E; 1];").unwrap_err();
-    syn::parse_str::<Stmt>("let TS(| A): TS;").unwrap_err();
-    syn::parse_str::<Stmt>("let TS(|| A): TS;").unwrap_err();
-    syn::parse_str::<Stmt>("let NS { f: | A }: NS;").unwrap_err();
-    syn::parse_str::<Stmt>("let NS { f: || A }: NS;").unwrap_err();
-}