Repository: dtolnay/syn
Pull request #1261 (2022-12-11 02:35:54)

Combine Stmt::Expr and Stmt::Semi into one variant

dtolnay: ```console
error: deref on an immutable reference
    --> src/gen/visit.rs:3209:31
     |
3209 |             if let Some(it) = &*_binding_1 {
     |                               ^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `_binding_1`
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
     = note: `-D clippy::borrow-deref-ref` implied by `-D clippy::all`
```

Patch:
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index fed88db4..4c1cef50 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -1788,8 +1788,7 @@ impl Clone for Stmt {
         match self {
             Stmt::Local(v0) => Stmt::Local(v0.clone()),
             Stmt::Item(v0) => Stmt::Item(v0.clone()),
-            Stmt::Expr(v0) => Stmt::Expr(v0.clone()),
-            Stmt::Semi(v0, v1) => Stmt::Semi(v0.clone(), v1.clone()),
+            Stmt::Expr(v0, v1) => Stmt::Expr(v0.clone(), v1.clone()),
         }
     }
 }
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index e48b0d52..b9eb74de 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -2429,14 +2429,9 @@ impl Debug for Stmt {
                 formatter.field(v0);
                 formatter.finish()
             }
-            Stmt::Expr(v0) => {
+            Stmt::Expr(v0, v1) => {
                 let mut formatter = formatter.debug_tuple("Expr");
                 formatter.field(v0);
-                formatter.finish()
-            }
-            Stmt::Semi(v0, v1) => {
-                let mut formatter = formatter.debug_tuple("Semi");
-                formatter.field(v0);
                 formatter.field(v1);
                 formatter.finish()
             }
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index 20acb809..b3b10b2a 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -1715,8 +1715,9 @@ impl PartialEq for Stmt {
         match (self, other) {
             (Stmt::Local(self0), Stmt::Local(other0)) => self0 == other0,
             (Stmt::Item(self0), Stmt::Item(other0)) => self0 == other0,
-            (Stmt::Expr(self0), Stmt::Expr(other0)) => self0 == other0,
-            (Stmt::Semi(self0, _), Stmt::Semi(other0, _)) => self0 == other0,
+            (Stmt::Expr(self0, self1), Stmt::Expr(other0, other1)) => {
+                self0 == other0 && self1 == other1
+            }
             _ => false,
         }
     }
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index e7a5f347..3e35d280 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -2840,11 +2840,10 @@ where
     match node {
         Stmt::Local(_binding_0) => Stmt::Local(f.fold_local(_binding_0)),
         Stmt::Item(_binding_0) => Stmt::Item(f.fold_item(_binding_0)),
-        Stmt::Expr(_binding_0) => Stmt::Expr(f.fold_expr(_binding_0)),
-        Stmt::Semi(_binding_0, _binding_1) => {
-            Stmt::Semi(
+        Stmt::Expr(_binding_0, _binding_1) => {
+            Stmt::Expr(
                 f.fold_expr(_binding_0),
-                Token![;](tokens_helper(f, &_binding_1.spans)),
+                (_binding_1).map(|it| Token![;](tokens_helper(f, &it.spans))),
             )
         }
     }
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index d0400e19..65093541 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -2275,13 +2275,10 @@ impl Hash for Stmt {
                 state.write_u8(1u8);
                 v0.hash(state);
             }
-            Stmt::Expr(v0) => {
+            Stmt::Expr(v0, v1) => {
                 state.write_u8(2u8);
                 v0.hash(state);
-            }
-            Stmt::Semi(v0, _) => {
-                state.write_u8(3u8);
-                v0.hash(state);
+                v1.hash(state);
             }
         }
     }
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index 19ddd2e7..c45ded63 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -3204,12 +3204,11 @@ where
         Stmt::Item(_binding_0) => {
             v.visit_item(_binding_0);
         }
-        Stmt::Expr(_binding_0) => {
+        Stmt::Expr(_binding_0, _binding_1) => {
             v.visit_expr(_binding_0);
-        }
-        Stmt::Semi(_binding_0, _binding_1) => {
-            v.visit_expr(_binding_0);
-            tokens_helper(v, &_binding_1.spans);
+            if let Some(it) = _binding_1 {
+                tokens_helper(v, &it.spans);
+            }
         }
     }
 }
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index 239709d1..25437cae 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -3204,12 +3204,11 @@ where
         Stmt::Item(_binding_0) => {
             v.visit_item_mut(_binding_0);
         }
-        Stmt::Expr(_binding_0) => {
+        Stmt::Expr(_binding_0, _binding_1) => {
             v.visit_expr_mut(_binding_0);
-        }
-        Stmt::Semi(_binding_0, _binding_1) => {
-            v.visit_expr_mut(_binding_0);
-            tokens_helper(v, &mut _binding_1.spans);
+            if let Some(it) = _binding_1 {
+                tokens_helper(v, &mut it.spans);
+            }
         }
     }
 }
diff --git a/src/stmt.rs b/src/stmt.rs
index 58bd013e..2a62b918 100644
--- a/src/stmt.rs
+++ b/src/stmt.rs
@@ -24,11 +24,8 @@ ast_enum! {
         /// An item definition.
         Item(Item),
 
-        /// Expr without trailing semicolon.
-        Expr(Expr),
-
-        /// Expression with trailing semicolon.
-        Semi(Expr, Token![;]),
+        /// Expression, with or without trailing semicolon.
+        Expr(Expr, Option<Token![;]>),
     }
 }
 
@@ -110,14 +107,14 @@ pub mod parsing {
         pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
             let mut stmts = Vec::new();
             loop {
-                while let Some(semi) = input.parse::<Option<Token![;]>>()? {
-                    stmts.push(Stmt::Semi(Expr::Verbatim(TokenStream::new()), semi));
+                while let semi @ Some(_) = input.parse()? {
+                    stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
                 }
                 if input.is_empty() {
                     break;
                 }
                 let s = parse_stmt(input, true)?;
-                let requires_semicolon = if let Stmt::Expr(s) = &s {
+                let requires_semicolon = if let Stmt::Expr(s, None) = &s {
                     expr::requires_terminator(s)
                 } else {
                     false
@@ -253,7 +250,7 @@ pub mod parsing {
                 content.call(Block::parse_within)?;
                 let verbatim = Expr::Verbatim(verbatim::between(begin, input));
                 let semi_token: Token![;] = input.parse()?;
-                return Ok(Stmt::Semi(verbatim, semi_token));
+                return Ok(Stmt::Expr(verbatim, Some(semi_token)));
             }
 
             Some((eq_token, Box::new(init)))
@@ -291,12 +288,12 @@ pub mod parsing {
         attrs.extend(attr_target.replace_attrs(Vec::new()));
         attr_target.replace_attrs(attrs);
 
-        if input.peek(Token![;]) {
-            return Ok(Stmt::Semi(e, input.parse()?));
+        if let semi @ Some(_) = input.parse()? {
+            return Ok(Stmt::Expr(e, semi));
         }
 
         if allow_nosemi || !expr::requires_terminator(&e) {
-            Ok(Stmt::Expr(e))
+            Ok(Stmt::Expr(e, None))
         } else {
             Err(input.error("expected semicolon"))
         }
@@ -324,8 +321,7 @@ mod printing {
             match self {
                 Stmt::Local(local) => local.to_tokens(tokens),
                 Stmt::Item(item) => item.to_tokens(tokens),
-                Stmt::Expr(expr) => expr.to_tokens(tokens),
-                Stmt::Semi(expr, semi) => {
+                Stmt::Expr(expr, semi) => {
                     expr.to_tokens(tokens);
                     semi.to_tokens(tokens);
                 }
diff --git a/syn.json b/syn.json
index 8be5cd58..13fb0703 100644
--- a/syn.json
+++ b/syn.json
@@ -4447,16 +4447,13 @@
           }
         ],
         "Expr": [
-          {
-            "syn": "Expr"
-          }
-        ],
-        "Semi": [
           {
             "syn": "Expr"
           },
           {
-            "token": "Semi"
+            "option": {
+              "token": "Semi"
+            }
           }
         ]
       }


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index cfd63d11..9f8c4076 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -4490,16 +4490,30 @@ impl Debug for Lite<syn::Stmt> {
                 formatter.write_str(")")?;
                 Ok(())
             }
-            syn::Stmt::Expr(_val) => {
-                formatter.write_str("Expr")?;
-                formatter.write_str("(")?;
-                Debug::fmt(Lite(_val), formatter)?;
-                formatter.write_str(")")?;
-                Ok(())
-            }
-            syn::Stmt::Semi(_v0, _v1) => {
-                let mut formatter = formatter.debug_tuple("Semi");
+            syn::Stmt::Expr(_v0, _v1) => {
+                let mut formatter = formatter.debug_tuple("Expr");
                 formatter.field(Lite(_v0));
+                formatter
+                    .field({
+                        #[derive(RefCast)]
+                        #[repr(transparent)]
+                        struct Print(Option<syn::token::Semi>);
+                        impl Debug for Print {
+                            fn fmt(
+                                &self,
+                                formatter: &mut fmt::Formatter,
+                            ) -> fmt::Result {
+                                match &self.0 {
+                                    Some(_val) => {
+                                        formatter.write_str("Some")?;
+                                        Ok(())
+                                    }
+                                    None => formatter.write_str("None"),
+                                }
+                            }
+                        }
+                        Print::ref_cast(_v1)
+                    });
                 formatter.finish()
             }
         }
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
index dbcd74f1..96441186 100644
--- a/tests/test_precedence.rs
+++ b/tests/test_precedence.rs
@@ -395,14 +395,8 @@ fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
         fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
             match stmt {
                 // Don't wrap toplevel expressions in statements.
-                Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
-                Stmt::Semi(e, semi) => {
-                    if let Expr::Verbatim(_) = e {
-                        Stmt::Semi(e, semi)
-                    } else {
-                        Stmt::Semi(fold_expr(self, e), semi)
-                    }
-                }
+                Stmt::Expr(Expr::Verbatim(_), Some(_)) => stmt,
+                Stmt::Expr(e, semi) => Stmt::Expr(fold_expr(self, e), semi),
                 s => s,
             }
         }