Repository: dtolnay/syn
Pull request #1295 (2023-01-16 06:12:54)

Add Expr::Infer variant for inferred const generic arguments
Tracking issue: https://github.com/rust-lang/rust/issues/85077


Patch:
diff --git a/src/expr.rs b/src/expr.rs
index ff36971c..6345575c 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -154,6 +154,9 @@ ast_enum_of_structs! {
         /// A square bracketed indexing expression: `vector[2]`.
         Index(ExprIndex),
 
+        /// The inferred value of a const generic argument, denoted `_`.
+        Infer(ExprInfer),
+
         /// A `let` guard: `let Some(x) = opt`.
         Let(ExprLet),
 
@@ -473,6 +476,14 @@ ast_struct! {
     }
 }
 
+ast_struct! {
+    /// The inferred value of a const generic argument, denoted `_`.
+    pub struct ExprInfer #full {
+        pub attrs: Vec<Attribute>,
+        pub underscore_token: Token![_],
+    }
+}
+
 ast_struct! {
     /// A `let` guard: `let Some(x) = opt`.
     #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
@@ -740,6 +751,7 @@ impl Expr {
             | Expr::Group(ExprGroup { attrs, .. })
             | Expr::If(ExprIf { attrs, .. })
             | Expr::Index(ExprIndex { attrs, .. })
+            | Expr::Infer(ExprInfer { attrs, .. })
             | Expr::Let(ExprLet { attrs, .. })
             | Expr::Lit(ExprLit { attrs, .. })
             | Expr::Loop(ExprLoop { attrs, .. })
@@ -1005,8 +1017,6 @@ pub(crate) mod parsing {
     use crate::parse::ParseBuffer;
     use crate::parse::{Parse, ParseStream, Result};
     use crate::path;
-    #[cfg(feature = "full")]
-    use proc_macro2::TokenTree;
     use std::cmp::Ordering;
 
     crate::custom_keyword!(raw);
@@ -1687,9 +1697,7 @@ pub(crate) mod parsing {
         } else if input.peek(Token![..]) {
             expr_range(input, allow_struct).map(Expr::Range)
         } else if input.peek(Token![_]) {
-            Ok(Expr::Verbatim(TokenStream::from(
-                input.parse::<TokenTree>()?,
-            )))
+            input.parse().map(Expr::Infer)
         } else if input.peek(Lifetime) {
             let the_label: Label = input.parse()?;
             let mut expr = if input.peek(Token![while]) {
@@ -2103,6 +2111,17 @@ pub(crate) mod parsing {
         Ok((else_token, Box::new(else_branch)))
     }
 
+    #[cfg(feature = "full")]
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
+    impl Parse for ExprInfer {
+        fn parse(input: ParseStream) -> Result<Self> {
+            Ok(ExprInfer {
+                attrs: input.call(Attribute::parse_outer)?,
+                underscore_token: input.parse()?,
+            })
+        }
+    }
+
     #[cfg(feature = "full")]
     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for ExprForLoop {
@@ -3293,6 +3312,15 @@ pub(crate) mod printing {
         }
     }
 
+    #[cfg(feature = "full")]
+    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
+    impl ToTokens for ExprInfer {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            outer_attrs_to_tokens(&self.attrs, tokens);
+            self.underscore_token.to_tokens(tokens);
+        }
+    }
+
     #[cfg(feature = "full")]
     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
     impl ToTokens for RangeLimits {
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index d30cb857..7100fa42 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -236,6 +236,8 @@ impl Clone for Expr {
             Expr::If(v0) => Expr::If(v0.clone()),
             Expr::Index(v0) => Expr::Index(v0.clone()),
             #[cfg(feature = "full")]
+            Expr::Infer(v0) => Expr::Infer(v0.clone()),
+            #[cfg(feature = "full")]
             Expr::Let(v0) => Expr::Let(v0.clone()),
             Expr::Lit(v0) => Expr::Lit(v0.clone()),
             #[cfg(feature = "full")]
@@ -514,6 +516,16 @@ impl Clone for ExprIndex {
 }
 #[cfg(feature = "full")]
 #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
+impl Clone for ExprInfer {
+    fn clone(&self) -> Self {
+        ExprInfer {
+            attrs: self.attrs.clone(),
+            underscore_token: self.underscore_token.clone(),
+        }
+    }
+}
+#[cfg(feature = "full")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
 impl Clone for ExprLet {
     fn clone(&self) -> Self {
         ExprLet {
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index bb59cf1c..de0c9de3 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -466,6 +466,12 @@ impl Debug for Expr {
                 formatter.finish()
             }
             #[cfg(feature = "full")]
+            Expr::Infer(v0) => {
+                let mut formatter = formatter.debug_tuple("Infer");
+                formatter.field(v0);
+                formatter.finish()
+            }
+            #[cfg(feature = "full")]
             Expr::Let(v0) => {
                 let mut formatter = formatter.debug_tuple("Let");
                 formatter.field(v0);
@@ -832,6 +838,16 @@ impl Debug for ExprIndex {
 }
 #[cfg(feature = "full")]
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+impl Debug for ExprInfer {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let mut formatter = formatter.debug_struct("ExprInfer");
+        formatter.field("attrs", &self.attrs);
+        formatter.field("underscore_token", &self.underscore_token);
+        formatter.finish()
+    }
+}
+#[cfg(feature = "full")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl Debug for ExprLet {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("ExprLet");
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index 936eede7..b282c7a7 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -258,6 +258,8 @@ impl PartialEq for Expr {
             (Expr::If(self0), Expr::If(other0)) => self0 == other0,
             (Expr::Index(self0), Expr::Index(other0)) => self0 == other0,
             #[cfg(feature = "full")]
+            (Expr::Infer(self0), Expr::Infer(other0)) => self0 == other0,
+            #[cfg(feature = "full")]
             (Expr::Let(self0), Expr::Let(other0)) => self0 == other0,
             (Expr::Lit(self0), Expr::Lit(other0)) => self0 == other0,
             #[cfg(feature = "full")]
@@ -506,6 +508,16 @@ impl PartialEq for ExprIndex {
 }
 #[cfg(feature = "full")]
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+impl Eq for ExprInfer {}
+#[cfg(feature = "full")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+impl PartialEq for ExprInfer {
+    fn eq(&self, other: &Self) -> bool {
+        self.attrs == other.attrs
+    }
+}
+#[cfg(feature = "full")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl Eq for ExprLet {}
 #[cfg(feature = "full")]
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index 89ba5b77..3f118a9a 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -179,6 +179,10 @@ pub trait Fold {
         fold_expr_index(self, i)
     }
     #[cfg(feature = "full")]
+    fn fold_expr_infer(&mut self, i: ExprInfer) -> ExprInfer {
+        fold_expr_infer(self, i)
+    }
+    #[cfg(feature = "full")]
     fn fold_expr_let(&mut self, i: ExprLet) -> ExprLet {
         fold_expr_let(self, i)
     }
@@ -1104,6 +1108,7 @@ where
         Expr::Group(_binding_0) => Expr::Group(full!(f.fold_expr_group(_binding_0))),
         Expr::If(_binding_0) => Expr::If(full!(f.fold_expr_if(_binding_0))),
         Expr::Index(_binding_0) => Expr::Index(f.fold_expr_index(_binding_0)),
+        Expr::Infer(_binding_0) => Expr::Infer(full!(f.fold_expr_infer(_binding_0))),
         Expr::Let(_binding_0) => Expr::Let(full!(f.fold_expr_let(_binding_0))),
         Expr::Lit(_binding_0) => Expr::Lit(f.fold_expr_lit(_binding_0)),
         Expr::Loop(_binding_0) => Expr::Loop(full!(f.fold_expr_loop(_binding_0))),
@@ -1375,6 +1380,16 @@ where
     }
 }
 #[cfg(feature = "full")]
+pub fn fold_expr_infer<F>(f: &mut F, node: ExprInfer) -> ExprInfer
+where
+    F: Fold + ?Sized,
+{
+    ExprInfer {
+        attrs: FoldHelper::lift(node.attrs, |it| f.fold_attribute(it)),
+        underscore_token: Token![_](tokens_helper(f, &node.underscore_token.spans)),
+    }
+}
+#[cfg(feature = "full")]
 pub fn fold_expr_let<F>(f: &mut F, node: ExprLet) -> ExprLet
 where
     F: Fold + ?Sized,
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index 2e0dd484..2d861d2d 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -398,108 +398,113 @@ impl Hash for Expr {
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
-            Expr::Let(v0) => {
+            Expr::Infer(v0) => {
                 state.write_u8(19u8);
                 v0.hash(state);
             }
-            Expr::Lit(v0) => {
+            #[cfg(feature = "full")]
+            Expr::Let(v0) => {
                 state.write_u8(20u8);
                 v0.hash(state);
             }
+            Expr::Lit(v0) => {
+                state.write_u8(21u8);
+                v0.hash(state);
+            }
             #[cfg(feature = "full")]
             Expr::Loop(v0) => {
-                state.write_u8(21u8);
+                state.write_u8(22u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Macro(v0) => {
-                state.write_u8(22u8);
+                state.write_u8(23u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Match(v0) => {
-                state.write_u8(23u8);
+                state.write_u8(24u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::MethodCall(v0) => {
-                state.write_u8(24u8);
+                state.write_u8(25u8);
                 v0.hash(state);
             }
             Expr::Paren(v0) => {
-                state.write_u8(25u8);
+                state.write_u8(26u8);
                 v0.hash(state);
             }
             Expr::Path(v0) => {
-                state.write_u8(26u8);
+                state.write_u8(27u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Range(v0) => {
-                state.write_u8(27u8);
+                state.write_u8(28u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Reference(v0) => {
-                state.write_u8(28u8);
+                state.write_u8(29u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Repeat(v0) => {
-                state.write_u8(29u8);
+                state.write_u8(30u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Return(v0) => {
-                state.write_u8(30u8);
+                state.write_u8(31u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Struct(v0) => {
-                state.write_u8(31u8);
+                state.write_u8(32u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Try(v0) => {
-                state.write_u8(32u8);
+                state.write_u8(33u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::TryBlock(v0) => {
-                state.write_u8(33u8);
+                state.write_u8(34u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Tuple(v0) => {
-                state.write_u8(34u8);
+                state.write_u8(35u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Type(v0) => {
-                state.write_u8(35u8);
+                state.write_u8(36u8);
                 v0.hash(state);
             }
             Expr::Unary(v0) => {
-                state.write_u8(36u8);
+                state.write_u8(37u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Unsafe(v0) => {
-                state.write_u8(37u8);
+                state.write_u8(38u8);
                 v0.hash(state);
             }
             Expr::Verbatim(v0) => {
-                state.write_u8(38u8);
+                state.write_u8(39u8);
                 TokenStreamHelper(v0).hash(state);
             }
             #[cfg(feature = "full")]
             Expr::While(v0) => {
-                state.write_u8(39u8);
+                state.write_u8(40u8);
                 v0.hash(state);
             }
             #[cfg(feature = "full")]
             Expr::Yield(v0) => {
-                state.write_u8(40u8);
+                state.write_u8(41u8);
                 v0.hash(state);
             }
             #[cfg(any(syn_no_non_exhaustive, not(feature = "full")))]
@@ -742,6 +747,16 @@ impl Hash for ExprIndex {
 }
 #[cfg(feature = "full")]
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
+impl Hash for ExprInfer {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: Hasher,
+    {
+        self.attrs.hash(state);
+    }
+}
+#[cfg(feature = "full")]
+#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl Hash for ExprLet {
     fn hash<H>(&self, state: &mut H)
     where
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index 8502acd1..e099d85c 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -181,6 +181,10 @@ pub trait Visit<'ast> {
         visit_expr_index(self, i);
     }
     #[cfg(feature = "full")]
+    fn visit_expr_infer(&mut self, i: &'ast ExprInfer) {
+        visit_expr_infer(self, i);
+    }
+    #[cfg(feature = "full")]
     fn visit_expr_let(&mut self, i: &'ast ExprLet) {
         visit_expr_let(self, i);
     }
@@ -1151,6 +1155,9 @@ where
         Expr::Index(_binding_0) => {
             v.visit_expr_index(_binding_0);
         }
+        Expr::Infer(_binding_0) => {
+            full!(v.visit_expr_infer(_binding_0));
+        }
         Expr::Let(_binding_0) => {
             full!(v.visit_expr_let(_binding_0));
         }
@@ -1498,6 +1505,16 @@ where
     v.visit_expr(&*node.index);
 }
 #[cfg(feature = "full")]
+pub fn visit_expr_infer<'ast, V>(v: &mut V, node: &'ast ExprInfer)
+where
+    V: Visit<'ast> + ?Sized,
+{
+    for it in &node.attrs {
+        v.visit_attribute(it);
+    }
+    tokens_helper(v, &node.underscore_token.spans);
+}
+#[cfg(feature = "full")]
 pub fn visit_expr_let<'ast, V>(v: &mut V, node: &'ast ExprLet)
 where
     V: Visit<'ast> + ?Sized,
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index b06f6e3e..58464157 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -182,6 +182,10 @@ pub trait VisitMut {
         visit_expr_index_mut(self, i);
     }
     #[cfg(feature = "full")]
+    fn visit_expr_infer_mut(&mut self, i: &mut ExprInfer) {
+        visit_expr_infer_mut(self, i);
+    }
+    #[cfg(feature = "full")]
     fn visit_expr_let_mut(&mut self, i: &mut ExprLet) {
         visit_expr_let_mut(self, i);
     }
@@ -1152,6 +1156,9 @@ where
         Expr::Index(_binding_0) => {
             v.visit_expr_index_mut(_binding_0);
         }
+        Expr::Infer(_binding_0) => {
+            full!(v.visit_expr_infer_mut(_binding_0));
+        }
         Expr::Let(_binding_0) => {
             full!(v.visit_expr_let_mut(_binding_0));
         }
@@ -1499,6 +1506,16 @@ where
     v.visit_expr_mut(&mut *node.index);
 }
 #[cfg(feature = "full")]
+pub fn visit_expr_infer_mut<V>(v: &mut V, node: &mut ExprInfer)
+where
+    V: VisitMut + ?Sized,
+{
+    for it in &mut node.attrs {
+        v.visit_attribute_mut(it);
+    }
+    tokens_helper(v, &mut node.underscore_token.spans);
+}
+#[cfg(feature = "full")]
 pub fn visit_expr_let_mut<V>(v: &mut V, node: &mut ExprLet)
 where
     V: VisitMut + ?Sized,
diff --git a/src/lib.rs b/src/lib.rs
index 024286aa..69768e81 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -349,10 +349,10 @@ pub use crate::expr::{
 pub use crate::expr::{
     Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary, ExprBlock,
     ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField,
-    ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch,
-    ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn,
-    ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile,
-    ExprYield, Index, Member,
+    ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro,
+    ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat,
+    ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe,
+    ExprWhile, ExprYield, Index, Member,
 };
 
 #[cfg(feature = "parsing")]
diff --git a/syn.json b/syn.json
index b9cc89f1..efbec7d1 100644
--- a/syn.json
+++ b/syn.json
@@ -663,6 +663,11 @@
             "syn": "ExprIndex"
           }
         ],
+        "Infer": [
+          {
+            "syn": "ExprInfer"
+          }
+        ],
         "Let": [
           {
             "syn": "ExprLet"
@@ -1338,6 +1343,24 @@
         }
       }
     },
+    {
+      "ident": "ExprInfer",
+      "features": {
+        "any": [
+          "full"
+        ]
+      },
+      "fields": {
+        "attrs": {
+          "vec": {
+            "syn": "Attribute"
+          }
+        },
+        "underscore_token": {
+          "token": "Underscore"
+        }
+      }
+    },
     {
       "ident": "ExprLet",
       "features": {


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index 0351c298..d08b6452 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -769,6 +769,13 @@ impl Debug for Lite<syn::Expr> {
                 formatter.field("index", Lite(&_val.index));
                 formatter.finish()
             }
+            syn::Expr::Infer(_val) => {
+                let mut formatter = formatter.debug_struct("Expr::Infer");
+                if !_val.attrs.is_empty() {
+                    formatter.field("attrs", Lite(&_val.attrs));
+                }
+                formatter.finish()
+            }
             syn::Expr::Let(_val) => {
                 let mut formatter = formatter.debug_struct("Expr::Let");
                 if !_val.attrs.is_empty() {
@@ -1546,6 +1553,16 @@ impl Debug for Lite<syn::ExprIndex> {
         formatter.finish()
     }
 }
+impl Debug for Lite<syn::ExprInfer> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let _val = &self.value;
+        let mut formatter = formatter.debug_struct("ExprInfer");
+        if !_val.attrs.is_empty() {
+            formatter.field("attrs", Lite(&_val.attrs));
+        }
+        formatter.finish()
+    }
+}
 impl Debug for Lite<syn::ExprLet> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let _val = &self.value;