Repository: dtolnay/syn
Pull request #1290 (2023-01-16 04:10:09)

Store bound lifetimes for Expr::Closure
Tracking issue: https://github.com/rust-lang/rust/issues/97362


Patch:
diff --git a/src/expr.rs b/src/expr.rs
index 43c4154b..772f2494 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -374,6 +374,7 @@ ast_struct! {
     #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
     pub struct ExprClosure #full {
         pub attrs: Vec<Attribute>,
+        pub lifetimes: Option<BoundLifetimes>,
         pub movability: Option<Token![static]>,
         pub asyncness: Option<Token![async]>,
         pub capture: Option<Token![move]>,
@@ -1633,20 +1634,14 @@ pub(crate) mod parsing {
         } else if input.peek(Token![try]) && input.peek2(token::Brace) {
             input.parse().map(Expr::TryBlock)
         } else if input.peek(Token![|])
-            || input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))
-            || input.peek(Token![static])
             || input.peek(Token![move])
+            || input.peek(Token![for])
+                && input.peek2(Token![<])
+                && (input.peek3(Lifetime) || input.peek3(Token![>]))
+            || input.peek(Token![static])
+            || input.peek(Token![async]) && (input.peek2(Token![|]) || input.peek2(Token![move]))
         {
             expr_closure(input, allow_struct).map(Expr::Closure)
-        } else if input.peek(Token![for])
-            && input.peek2(Token![<])
-            && (input.peek3(Lifetime) || input.peek3(Token![>]))
-        {
-            let begin = input.fork();
-            input.parse::<BoundLifetimes>()?;
-            expr_closure(input, allow_struct)?;
-            let verbatim = verbatim::between(begin, input);
-            Ok(Expr::Verbatim(verbatim))
         } else if input.peek(Ident)
             || input.peek(Token![::])
             || input.peek(Token![<])
@@ -2346,6 +2341,7 @@ pub(crate) mod parsing {
 
     #[cfg(feature = "full")]
     fn expr_closure(input: ParseStream, allow_struct: AllowStruct) -> Result<ExprClosure> {
+        let lifetimes: Option<BoundLifetimes> = input.parse()?;
         let movability: Option<Token![static]> = input.parse()?;
         let asyncness: Option<Token![async]> = input.parse()?;
         let capture: Option<Token![move]> = input.parse()?;
@@ -2385,6 +2381,7 @@ pub(crate) mod parsing {
 
         Ok(ExprClosure {
             attrs: Vec::new(),
+            lifetimes,
             movability,
             asyncness,
             capture,
@@ -3186,6 +3183,7 @@ pub(crate) mod printing {
     impl ToTokens for ExprClosure {
         fn to_tokens(&self, tokens: &mut TokenStream) {
             outer_attrs_to_tokens(&self.attrs, tokens);
+            self.lifetimes.to_tokens(tokens);
             self.movability.to_tokens(tokens);
             self.asyncness.to_tokens(tokens);
             self.capture.to_tokens(tokens);
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index b27b8747..57960d07 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -414,6 +414,7 @@ impl Clone for ExprClosure {
     fn clone(&self) -> Self {
         ExprClosure {
             attrs: self.attrs.clone(),
+            lifetimes: self.lifetimes.clone(),
             movability: self.movability.clone(),
             asyncness: self.asyncness.clone(),
             capture: self.capture.clone(),
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index e2a0963c..d1cb3fa2 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -732,6 +732,7 @@ impl Debug for ExprClosure {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("ExprClosure");
         formatter.field("attrs", &self.attrs);
+        formatter.field("lifetimes", &self.lifetimes);
         formatter.field("movability", &self.movability);
         formatter.field("asyncness", &self.asyncness);
         formatter.field("capture", &self.capture);
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index d9802f2b..c172e90c 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -423,10 +423,10 @@ impl Eq for ExprClosure {}
 #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
 impl PartialEq for ExprClosure {
     fn eq(&self, other: &Self) -> bool {
-        self.attrs == other.attrs && self.movability == other.movability
-            && self.asyncness == other.asyncness && self.capture == other.capture
-            && self.inputs == other.inputs && self.output == other.output
-            && self.body == other.body
+        self.attrs == other.attrs && self.lifetimes == other.lifetimes
+            && self.movability == other.movability && self.asyncness == other.asyncness
+            && self.capture == other.capture && self.inputs == other.inputs
+            && self.output == other.output && self.body == other.body
     }
 }
 #[cfg(feature = "full")]
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index da4424fb..8639aee4 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -1272,6 +1272,7 @@ where
 {
     ExprClosure {
         attrs: FoldHelper::lift(node.attrs, |it| f.fold_attribute(it)),
+        lifetimes: (node.lifetimes).map(|it| f.fold_bound_lifetimes(it)),
         movability: (node.movability)
             .map(|it| Token![static](tokens_helper(f, &it.span))),
         asyncness: (node.asyncness).map(|it| Token![async](tokens_helper(f, &it.span))),
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index f32fc248..347bc797 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -646,6 +646,7 @@ impl Hash for ExprClosure {
         H: Hasher,
     {
         self.attrs.hash(state);
+        self.lifetimes.hash(state);
         self.movability.hash(state);
         self.asyncness.hash(state);
         self.capture.hash(state);
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index 34bd0b49..f4122eda 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -1378,6 +1378,9 @@ where
     for it in &node.attrs {
         v.visit_attribute(it);
     }
+    if let Some(it) = &node.lifetimes {
+        v.visit_bound_lifetimes(it);
+    }
     if let Some(it) = &node.movability {
         tokens_helper(v, &it.span);
     }
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index d3216ee3..631aadd8 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -1379,6 +1379,9 @@ where
     for it in &mut node.attrs {
         v.visit_attribute_mut(it);
     }
+    if let Some(it) = &mut node.lifetimes {
+        v.visit_bound_lifetimes_mut(it);
+    }
     if let Some(it) = &mut node.movability {
         tokens_helper(v, &mut it.span);
     }
diff --git a/syn.json b/syn.json
index adea26a5..a7290463 100644
--- a/syn.json
+++ b/syn.json
@@ -1089,6 +1089,11 @@
             "syn": "Attribute"
           }
         },
+        "lifetimes": {
+          "option": {
+            "syn": "BoundLifetimes"
+          }
+        },
         "movability": {
           "option": {
             "token": "Static"


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index d4fc5d67..65a52820 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -590,6 +590,22 @@ impl Debug for Lite<syn::Expr> {
                 if !_val.attrs.is_empty() {
                     formatter.field("attrs", Lite(&_val.attrs));
                 }
+                if let Some(val) = &_val.lifetimes {
+                    #[derive(RefCast)]
+                    #[repr(transparent)]
+                    struct Print(syn::BoundLifetimes);
+                    impl Debug for Print {
+                        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                            formatter.write_str("Some")?;
+                            let _val = &self.0;
+                            formatter.write_str("(")?;
+                            Debug::fmt(Lite(_val), formatter)?;
+                            formatter.write_str(")")?;
+                            Ok(())
+                        }
+                    }
+                    formatter.field("lifetimes", Print::ref_cast(val));
+                }
                 if let Some(val) = &_val.movability {
                     #[derive(RefCast)]
                     #[repr(transparent)]
@@ -1301,6 +1317,22 @@ impl Debug for Lite<syn::ExprClosure> {
         if !_val.attrs.is_empty() {
             formatter.field("attrs", Lite(&_val.attrs));
         }
+        if let Some(val) = &_val.lifetimes {
+            #[derive(RefCast)]
+            #[repr(transparent)]
+            struct Print(syn::BoundLifetimes);
+            impl Debug for Print {
+                fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                    formatter.write_str("Some")?;
+                    let _val = &self.0;
+                    formatter.write_str("(")?;
+                    Debug::fmt(Lite(_val), formatter)?;
+                    formatter.write_str(")")?;
+                    Ok(())
+                }
+            }
+            formatter.field("lifetimes", Print::ref_cast(val));
+        }
         if let Some(val) = &_val.movability {
             #[derive(RefCast)]
             #[repr(transparent)]