diff --git a/codegen/src/fold.rs b/codegen/src/fold.rs index 13a5390a9e..84c57f59dd 100644 --- a/codegen/src/fold.rs +++ b/codegen/src/fold.rs @@ -1,4 +1,4 @@ -use crate::{file, full, gen}; +use crate::{file, full, r#gen}; use anyhow::Result; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; @@ -8,7 +8,7 @@ use syn_codegen::{Data, Definitions, Features, Node, Type}; const FOLD_SRC: &str = "src/gen/fold.rs"; fn simple_visit(item: &str, name: &TokenStream) -> TokenStream { - let ident = gen::under_name(item); + let ident = r#gen::under_name(item); let method = format_ident!("fold_{}", ident); quote! { f.#method(#name) @@ -73,13 +73,13 @@ fn visit( } Some(res) } - Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), + Type::Ext(t) if r#gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), Type::Ext(_) | Type::Std(_) | Type::Token(_) | Type::Group(_) => None, } } fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) { - let under_name = gen::under_name(&s.ident); + let under_name = r#gen::under_name(&s.ident); let ty = Ident::new(&s.ident, Span::call_site()); let fold_fn = format_ident!("fold_{}", under_name); @@ -182,7 +182,7 @@ fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Defi } let fold_span_only = - s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str()); + s.data == Data::Private && !r#gen::TERMINAL_TYPES.contains(&s.ident.as_str()); if fold_span_only { fold_impl = quote! { let span = f.fold_span(node.span()); @@ -209,7 +209,7 @@ fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Defi } pub fn generate(defs: &Definitions) -> Result<()> { - let (traits, impls) = gen::traverse(defs, node); + let (traits, impls) = r#gen::traverse(defs, node); let full_macro = full::get_macro(); file::write( FOLD_SRC, @@ -223,7 +223,7 @@ pub fn generate(defs: &Definitions) -> Result<()> { )] #[cfg(any(feature = "full", feature = "derive"))] - use crate::gen::helper::fold::*; + use crate::r#gen::helper::fold::*; use crate::*; use proc_macro2::Span; diff --git a/codegen/src/main.rs b/codegen/src/main.rs index 8b8670c5e5..9264b2765f 100644 --- a/codegen/src/main.rs +++ b/codegen/src/main.rs @@ -25,7 +25,7 @@ mod eq; mod file; mod fold; mod full; -mod gen; +mod r#gen; mod hash; mod json; mod lookup; diff --git a/codegen/src/visit.rs b/codegen/src/visit.rs index 573798a1f5..5afebbbf6c 100644 --- a/codegen/src/visit.rs +++ b/codegen/src/visit.rs @@ -1,5 +1,5 @@ use crate::operand::{Borrowed, Operand, Owned}; -use crate::{file, full, gen}; +use crate::{file, full, r#gen}; use anyhow::Result; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; @@ -9,7 +9,7 @@ use syn_codegen::{Data, Definitions, Features, Node, Type}; const VISIT_SRC: &str = "src/gen/visit.rs"; fn simple_visit(item: &str, name: &Operand) -> TokenStream { - let ident = gen::under_name(item); + let ident = r#gen::under_name(item); let method = format_ident!("visit_{}", ident); let name = name.ref_tokens(); quote! { @@ -89,13 +89,13 @@ fn visit( } Some(res) } - Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), + Type::Ext(t) if r#gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), Type::Ext(_) | Type::Std(_) | Type::Token(_) | Type::Group(_) => None, } } fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) { - let under_name = gen::under_name(&s.ident); + let under_name = r#gen::under_name(&s.ident); let ty = Ident::new(&s.ident, Span::call_site()); let visit_fn = format_ident!("visit_{}", under_name); @@ -195,7 +195,7 @@ fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Defi } pub fn generate(defs: &Definitions) -> Result<()> { - let (traits, impls) = gen::traverse(defs, node); + let (traits, impls) = r#gen::traverse(defs, node); let full_macro = full::get_macro(); file::write( VISIT_SRC, diff --git a/codegen/src/visit_mut.rs b/codegen/src/visit_mut.rs index 5b96c87110..978bf2c330 100644 --- a/codegen/src/visit_mut.rs +++ b/codegen/src/visit_mut.rs @@ -1,5 +1,5 @@ use crate::operand::{Borrowed, Operand, Owned}; -use crate::{file, full, gen}; +use crate::{file, full, r#gen}; use anyhow::Result; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; @@ -9,7 +9,7 @@ use syn_codegen::{Data, Definitions, Features, Node, Type}; const VISIT_MUT_SRC: &str = "src/gen/visit_mut.rs"; fn simple_visit(item: &str, name: &Operand) -> TokenStream { - let ident = gen::under_name(item); + let ident = r#gen::under_name(item); let method = format_ident!("visit_{}_mut", ident); let name = name.ref_mut_tokens(); quote! { @@ -89,13 +89,13 @@ fn visit( } Some(res) } - Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), + Type::Ext(t) if r#gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), Type::Ext(_) | Type::Std(_) | Type::Token(_) | Type::Group(_) => None, } } fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) { - let under_name = gen::under_name(&s.ident); + let under_name = r#gen::under_name(&s.ident); let ty = Ident::new(&s.ident, Span::call_site()); let visit_mut_fn = format_ident!("visit_{}_mut", under_name); @@ -191,7 +191,7 @@ fn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Defi } pub fn generate(defs: &Definitions) -> Result<()> { - let (traits, impls) = gen::traverse(defs, node); + let (traits, impls) = r#gen::traverse(defs, node); let full_macro = full::get_macro(); file::write( VISIT_MUT_SRC, diff --git a/src/expr.rs b/src/expr.rs index 57b38150de..f6ee841ecc 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -131,6 +131,9 @@ ast_enum_of_structs! { /// A for loop: `for pat in expr { ... }`. ForLoop(ExprForLoop), + /// A gen block: `gen { }`. + Gen(ExprGen), + /// An expression contained within invisible delimiters. /// /// This variant is important for faithfully representing the precedence @@ -400,6 +403,18 @@ ast_struct! { } } +ast_struct! { + /// A gen block: `gen { }`. + #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] + pub struct ExprGen #full { + pub attrs: Vec, + pub async_token: Option, + pub gen_token: Token![gen], + pub capture: Option, + pub block: Block, + } +} + ast_struct! { /// An expression contained within invisible delimiters. /// @@ -699,6 +714,7 @@ impl Expr { | Expr::Continue(ExprContinue { attrs, .. }) | Expr::Field(ExprField { attrs, .. }) | Expr::ForLoop(ExprForLoop { attrs, .. }) + | Expr::Gen(ExprGen { attrs, .. }) | Expr::Group(ExprGroup { attrs, .. }) | Expr::If(ExprIf { attrs, .. }) | Expr::Index(ExprIndex { attrs, .. }) @@ -932,6 +948,7 @@ pub(crate) fn requires_terminator(expr: &Expr) -> bool { | Expr::Closure(_) | Expr::Continue(_) | Expr::Field(_) + | Expr::Gen(_) | Expr::Group(_) | Expr::Index(_) | Expr::Infer(_) @@ -1589,6 +1606,12 @@ pub(crate) mod parsing { && (input.peek2(token::Brace) || input.peek2(Token![move]) && input.peek3(token::Brace)) { input.parse().map(Expr::Async) + } else if (input.peek(Token![async]) + && input.peek2(Token![gen]) + && (input.peek3(token::Brace) || input.peek3(Token![move]) && input.peek4(token::Brace))) + || (input.peek(Token![gen]) && (input.peek2(token::Brace) || input.peek2(Token![move]) && input.peek3(token::Brace))) + { + input.parse().map(Expr::Gen) } else if input.peek(Token![try]) && input.peek2(token::Brace) { input.parse().map(Expr::TryBlock) } else if input.peek(Token![|]) @@ -2404,6 +2427,20 @@ pub(crate) mod parsing { } } + #[cfg(feature = "full")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] + impl Parse for ExprGen { + fn parse(input: ParseStream) -> Result { + Ok(ExprGen { + attrs: Vec::new(), + async_token: input.parse()?, + gen_token: input.parse()?, + capture: input.parse()?, + block: input.parse()?, + }) + } + } + #[cfg(feature = "full")] fn closure_arg(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -3106,6 +3143,18 @@ pub(crate) mod printing { } } + #[cfg(feature = "full")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] + impl ToTokens for ExprGen { + fn to_tokens(&self, tokens: &mut TokenStream) { + outer_attrs_to_tokens(&self.attrs, tokens); + self.async_token.to_tokens(tokens); + self.gen_token.to_tokens(tokens); + self.capture.to_tokens(tokens); + self.block.to_tokens(tokens); + } + } + #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for ExprGroup { fn to_tokens(&self, tokens: &mut TokenStream) { diff --git a/src/gen/clone.rs b/src/gen/clone.rs index d275f51145..41e38eb8ee 100644 --- a/src/gen/clone.rs +++ b/src/gen/clone.rs @@ -252,6 +252,8 @@ impl Clone for Expr { Expr::Field(v0) => Expr::Field(v0.clone()), #[cfg(feature = "full")] Expr::ForLoop(v0) => Expr::ForLoop(v0.clone()), + #[cfg(feature = "full")] + Expr::Gen(v0) => Expr::Gen(v0.clone()), Expr::Group(v0) => Expr::Group(v0.clone()), #[cfg(feature = "full")] Expr::If(v0) => Expr::If(v0.clone()), @@ -473,6 +475,19 @@ impl Clone for ExprForLoop { } } } +#[cfg(feature = "full")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))] +impl Clone for ExprGen { + fn clone(&self) -> Self { + ExprGen { + attrs: self.attrs.clone(), + async_token: self.async_token.clone(), + gen_token: self.gen_token.clone(), + capture: self.capture.clone(), + block: self.block.clone(), + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))] impl Clone for ExprGroup { @@ -1712,6 +1727,7 @@ impl Clone for Signature { Signature { constness: self.constness.clone(), asyncness: self.asyncness.clone(), + generator: self.generator.clone(), unsafety: self.unsafety.clone(), abi: self.abi.clone(), fn_token: self.fn_token.clone(), diff --git a/src/gen/debug.rs b/src/gen/debug.rs index 837fe99f4c..2c022df4d2 100644 --- a/src/gen/debug.rs +++ b/src/gen/debug.rs @@ -418,6 +418,8 @@ impl Debug for Expr { Expr::Field(v0) => v0.debug(formatter, "Field"), #[cfg(feature = "full")] Expr::ForLoop(v0) => v0.debug(formatter, "ForLoop"), + #[cfg(feature = "full")] + Expr::Gen(v0) => v0.debug(formatter, "Gen"), Expr::Group(v0) => v0.debug(formatter, "Group"), #[cfg(feature = "full")] Expr::If(v0) => v0.debug(formatter, "If"), @@ -713,6 +715,24 @@ impl Debug for ExprForLoop { self.debug(formatter, "ExprForLoop") } } +#[cfg(feature = "full")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] +impl Debug for ExprGen { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + impl ExprGen { + fn debug(&self, formatter: &mut fmt::Formatter, name: &str) -> fmt::Result { + let mut formatter = formatter.debug_struct(name); + formatter.field("attrs", &self.attrs); + formatter.field("async_token", &self.async_token); + formatter.field("gen_token", &self.gen_token); + formatter.field("capture", &self.capture); + formatter.field("block", &self.block); + formatter.finish() + } + } + self.debug(formatter, "ExprGen") + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Debug for ExprGroup { @@ -2393,6 +2413,7 @@ impl Debug for Signature { let mut formatter = formatter.debug_struct("Signature"); formatter.field("constness", &self.constness); formatter.field("asyncness", &self.asyncness); + formatter.field("generator", &self.generator); formatter.field("unsafety", &self.unsafety); formatter.field("abi", &self.abi); formatter.field("fn_token", &self.fn_token); diff --git a/src/gen/eq.rs b/src/gen/eq.rs index a7479c300b..d1b3fb5bbb 100644 --- a/src/gen/eq.rs +++ b/src/gen/eq.rs @@ -271,6 +271,8 @@ impl PartialEq for Expr { (Expr::Field(self0), Expr::Field(other0)) => self0 == other0, #[cfg(feature = "full")] (Expr::ForLoop(self0), Expr::ForLoop(other0)) => self0 == other0, + #[cfg(feature = "full")] + (Expr::Gen(self0), Expr::Gen(other0)) => self0 == other0, (Expr::Group(self0), Expr::Group(other0)) => self0 == other0, #[cfg(feature = "full")] (Expr::If(self0), Expr::If(other0)) => self0 == other0, @@ -468,6 +470,17 @@ impl PartialEq for ExprForLoop { && self.expr == other.expr && self.body == other.body } } +#[cfg(feature = "full")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] +impl Eq for ExprGen {} +#[cfg(feature = "full")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] +impl PartialEq for ExprGen { + fn eq(&self, other: &Self) -> bool { + self.attrs == other.attrs && self.async_token == other.async_token + && self.capture == other.capture && self.block == other.block + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Eq for ExprGroup {} @@ -1646,10 +1659,10 @@ impl Eq for Signature {} impl PartialEq for Signature { fn eq(&self, other: &Self) -> bool { self.constness == other.constness && self.asyncness == other.asyncness - && self.unsafety == other.unsafety && self.abi == other.abi - && self.ident == other.ident && self.generics == other.generics - && self.inputs == other.inputs && self.variadic == other.variadic - && self.output == other.output + && self.generator == other.generator && self.unsafety == other.unsafety + && self.abi == other.abi && self.ident == other.ident + && self.generics == other.generics && self.inputs == other.inputs + && self.variadic == other.variadic && self.output == other.output } } #[cfg(feature = "full")] diff --git a/src/gen/fold.rs b/src/gen/fold.rs index 8ea6c75fc9..4549ef798c 100644 --- a/src/gen/fold.rs +++ b/src/gen/fold.rs @@ -8,7 +8,7 @@ clippy::needless_pass_by_ref_mut, )] #[cfg(any(feature = "full", feature = "derive"))] -use crate::gen::helper::fold::*; +use crate::r#gen::helper::fold::*; use crate::*; use proc_macro2::Span; #[cfg(feature = "full")] @@ -168,6 +168,10 @@ pub trait Fold { fn fold_expr_for_loop(&mut self, i: ExprForLoop) -> ExprForLoop { fold_expr_for_loop(self, i) } + #[cfg(feature = "full")] + fn fold_expr_gen(&mut self, i: ExprGen) -> ExprGen { + fold_expr_gen(self, i) + } #[cfg(any(feature = "derive", feature = "full"))] fn fold_expr_group(&mut self, i: ExprGroup) -> ExprGroup { fold_expr_group(self, i) @@ -1031,6 +1035,7 @@ where Expr::ForLoop(_binding_0) => { Expr::ForLoop(full!(f.fold_expr_for_loop(_binding_0))) } + Expr::Gen(_binding_0) => Expr::Gen(full!(f.fold_expr_gen(_binding_0))), Expr::Group(_binding_0) => Expr::Group(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)), @@ -1238,6 +1243,19 @@ where body: f.fold_block(node.body), } } +#[cfg(feature = "full")] +pub fn fold_expr_gen(f: &mut F, node: ExprGen) -> ExprGen +where + F: Fold + ?Sized, +{ + ExprGen { + attrs: FoldHelper::lift(node.attrs, |it| f.fold_attribute(it)), + async_token: node.async_token, + gen_token: node.gen_token, + capture: node.capture, + block: f.fold_block(node.block), + } +} #[cfg(any(feature = "derive", feature = "full"))] pub fn fold_expr_group(f: &mut F, node: ExprGroup) -> ExprGroup where @@ -2614,6 +2632,7 @@ where Signature { constness: node.constness, asyncness: node.asyncness, + generator: node.generator, unsafety: node.unsafety, abi: (node.abi).map(|it| f.fold_abi(it)), fn_token: node.fn_token, diff --git a/src/gen/hash.rs b/src/gen/hash.rs index 40dfc57f30..f221b9c3ef 100644 --- a/src/gen/hash.rs +++ b/src/gen/hash.rs @@ -399,121 +399,126 @@ impl Hash for Expr { state.write_u8(13u8); v0.hash(state); } - Expr::Group(v0) => { + #[cfg(feature = "full")] + Expr::Gen(v0) => { state.write_u8(14u8); v0.hash(state); } + Expr::Group(v0) => { + state.write_u8(15u8); + v0.hash(state); + } #[cfg(feature = "full")] Expr::If(v0) => { - state.write_u8(15u8); + state.write_u8(16u8); v0.hash(state); } Expr::Index(v0) => { - state.write_u8(16u8); + state.write_u8(17u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Infer(v0) => { - state.write_u8(17u8); + state.write_u8(18u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Let(v0) => { - state.write_u8(18u8); + state.write_u8(19u8); v0.hash(state); } Expr::Lit(v0) => { - state.write_u8(19u8); + state.write_u8(20u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Loop(v0) => { - state.write_u8(20u8); + state.write_u8(21u8); v0.hash(state); } Expr::Macro(v0) => { - state.write_u8(21u8); + state.write_u8(22u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Match(v0) => { - state.write_u8(22u8); + state.write_u8(23u8); v0.hash(state); } #[cfg(feature = "full")] Expr::MethodCall(v0) => { - state.write_u8(23u8); + state.write_u8(24u8); v0.hash(state); } Expr::Paren(v0) => { - state.write_u8(24u8); + state.write_u8(25u8); v0.hash(state); } Expr::Path(v0) => { - state.write_u8(25u8); + state.write_u8(26u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Range(v0) => { - state.write_u8(26u8); + state.write_u8(27u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Reference(v0) => { - state.write_u8(27u8); + state.write_u8(28u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Repeat(v0) => { - state.write_u8(28u8); + state.write_u8(29u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Return(v0) => { - state.write_u8(29u8); + state.write_u8(30u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Struct(v0) => { - state.write_u8(30u8); + state.write_u8(31u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Try(v0) => { - state.write_u8(31u8); + state.write_u8(32u8); v0.hash(state); } #[cfg(feature = "full")] Expr::TryBlock(v0) => { - state.write_u8(32u8); + state.write_u8(33u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Tuple(v0) => { - state.write_u8(33u8); + state.write_u8(34u8); v0.hash(state); } Expr::Unary(v0) => { - state.write_u8(34u8); + state.write_u8(35u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Unsafe(v0) => { - state.write_u8(35u8); + state.write_u8(36u8); v0.hash(state); } Expr::Verbatim(v0) => { - state.write_u8(36u8); + state.write_u8(37u8); TokenStreamHelper(v0).hash(state); } #[cfg(feature = "full")] Expr::While(v0) => { - state.write_u8(37u8); + state.write_u8(38u8); v0.hash(state); } #[cfg(feature = "full")] Expr::Yield(v0) => { - state.write_u8(38u8); + state.write_u8(39u8); v0.hash(state); } #[cfg(not(feature = "full"))] @@ -694,6 +699,19 @@ impl Hash for ExprForLoop { self.body.hash(state); } } +#[cfg(feature = "full")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] +impl Hash for ExprGen { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.attrs.hash(state); + self.async_token.hash(state); + self.capture.hash(state); + self.block.hash(state); + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))] impl Hash for ExprGroup { @@ -2180,6 +2198,7 @@ impl Hash for Signature { { self.constness.hash(state); self.asyncness.hash(state); + self.generator.hash(state); self.unsafety.hash(state); self.abi.hash(state); self.ident.hash(state); diff --git a/src/gen/visit.rs b/src/gen/visit.rs index fe81fb63ce..1a02485d32 100644 --- a/src/gen/visit.rs +++ b/src/gen/visit.rs @@ -167,6 +167,10 @@ pub trait Visit<'ast> { fn visit_expr_for_loop(&mut self, i: &'ast ExprForLoop) { visit_expr_for_loop(self, i); } + #[cfg(feature = "full")] + fn visit_expr_gen(&mut self, i: &'ast ExprGen) { + visit_expr_gen(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] fn visit_expr_group(&mut self, i: &'ast ExprGroup) { visit_expr_group(self, i); @@ -1124,6 +1128,9 @@ where Expr::ForLoop(_binding_0) => { full!(v.visit_expr_for_loop(_binding_0)); } + Expr::Gen(_binding_0) => { + full!(v.visit_expr_gen(_binding_0)); + } Expr::Group(_binding_0) => { v.visit_expr_group(_binding_0); } @@ -1396,6 +1403,19 @@ where v.visit_expr(&*node.expr); v.visit_block(&node.body); } +#[cfg(feature = "full")] +pub fn visit_expr_gen<'ast, V>(v: &mut V, node: &'ast ExprGen) +where + V: Visit<'ast> + ?Sized, +{ + for it in &node.attrs { + v.visit_attribute(it); + } + skip!(node.async_token); + skip!(node.gen_token); + skip!(node.capture); + v.visit_block(&node.block); +} #[cfg(any(feature = "derive", feature = "full"))] pub fn visit_expr_group<'ast, V>(v: &mut V, node: &'ast ExprGroup) where @@ -2927,6 +2947,7 @@ where { skip!(node.constness); skip!(node.asyncness); + skip!(node.generator); skip!(node.unsafety); if let Some(it) = &node.abi { v.visit_abi(it); diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs index 9e7d16ff60..c52a4544d1 100644 --- a/src/gen/visit_mut.rs +++ b/src/gen/visit_mut.rs @@ -168,6 +168,10 @@ pub trait VisitMut { fn visit_expr_for_loop_mut(&mut self, i: &mut ExprForLoop) { visit_expr_for_loop_mut(self, i); } + #[cfg(feature = "full")] + fn visit_expr_gen_mut(&mut self, i: &mut ExprGen) { + visit_expr_gen_mut(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] fn visit_expr_group_mut(&mut self, i: &mut ExprGroup) { visit_expr_group_mut(self, i); @@ -1125,6 +1129,9 @@ where Expr::ForLoop(_binding_0) => { full!(v.visit_expr_for_loop_mut(_binding_0)); } + Expr::Gen(_binding_0) => { + full!(v.visit_expr_gen_mut(_binding_0)); + } Expr::Group(_binding_0) => { v.visit_expr_group_mut(_binding_0); } @@ -1397,6 +1404,19 @@ where v.visit_expr_mut(&mut *node.expr); v.visit_block_mut(&mut node.body); } +#[cfg(feature = "full")] +pub fn visit_expr_gen_mut(v: &mut V, node: &mut ExprGen) +where + V: VisitMut + ?Sized, +{ + for it in &mut node.attrs { + v.visit_attribute_mut(it); + } + skip!(node.async_token); + skip!(node.gen_token); + skip!(node.capture); + v.visit_block_mut(&mut node.block); +} #[cfg(any(feature = "derive", feature = "full"))] pub fn visit_expr_group_mut(v: &mut V, node: &mut ExprGroup) where @@ -2930,6 +2950,7 @@ where { skip!(node.constness); skip!(node.asyncness); + skip!(node.generator); skip!(node.unsafety); if let Some(it) = &mut node.abi { v.visit_abi_mut(it); diff --git a/src/ident.rs b/src/ident.rs index d0f4ba08db..0f07ce237d 100644 --- a/src/ident.rs +++ b/src/ident.rs @@ -61,7 +61,7 @@ mod parsing { // Based on https://doc.rust-lang.org/1.65.0/reference/keywords.html "abstract" | "as" | "async" | "await" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" | "dyn" | "else" | "enum" | - "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | + "extern" | "false" | "final" | "fn" | "for" | "gen" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override" | "priv" | "pub" | "ref" | "return" | "Self" | "self" | "static" | "struct" | "super" | "trait" | "true" | "try" | "type" | diff --git a/src/item.rs b/src/item.rs index ee91f5914c..0fec58ea23 100644 --- a/src/item.rs +++ b/src/item.rs @@ -779,6 +779,7 @@ ast_struct! { pub struct Signature { pub constness: Option, pub asyncness: Option, + pub generator: Option, pub unsafety: Option, pub abi: Option, pub fn_token: Token![fn], @@ -1447,6 +1448,7 @@ pub(crate) mod parsing { let fork = input.fork(); fork.parse::>().is_ok() && fork.parse::>().is_ok() + && fork.parse::>().is_ok() && fork.parse::>().is_ok() && fork.parse::>().is_ok() && fork.peek(Token![fn]) @@ -1457,6 +1459,7 @@ pub(crate) mod parsing { fn parse(input: ParseStream) -> Result { let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; + let generator: Option = input.parse()?; let unsafety: Option = input.parse()?; let abi: Option = input.parse()?; let fn_token: Token![fn] = input.parse()?; @@ -1473,6 +1476,7 @@ pub(crate) mod parsing { Ok(Signature { constness, asyncness, + generator, unsafety, abi, fn_token, @@ -2276,6 +2280,7 @@ pub(crate) mod parsing { return Ok(TraitItem::Verbatim(verbatim::between(&begin, input))); } } else if lookahead.peek(Token![async]) + || lookahead.peek(Token![gen]) || lookahead.peek(Token![unsafe]) || lookahead.peek(Token![extern]) || lookahead.peek(Token![fn]) @@ -3323,6 +3328,7 @@ mod printing { fn to_tokens(&self, tokens: &mut TokenStream) { self.constness.to_tokens(tokens); self.asyncness.to_tokens(tokens); + self.generator.to_tokens(tokens); self.unsafety.to_tokens(tokens); self.abi.to_tokens(tokens); self.fn_token.to_tokens(tokens); diff --git a/src/lib.rs b/src/lib.rs index 0341789e2d..4798c830d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -348,7 +348,7 @@ pub use crate::expr::{Arm, FieldValue, Label, RangeLimits}; #[cfg(any(feature = "full", feature = "derive"))] pub use crate::expr::{ Expr, ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, - ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprGroup, ExprIf, + ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprGen, ExprGroup, ExprIf, ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, Index, Member, @@ -494,7 +494,7 @@ mod verbatim; #[cfg(all(feature = "parsing", feature = "full"))] mod whitespace; -mod gen { +mod r#gen { /// Syntax tree traversal to transform the nodes of an owned syntax tree. /// /// Each method of the [`Fold`] trait is a hook that can be overridden to @@ -815,7 +815,7 @@ mod gen { } #[cfg(any(feature = "fold", feature = "visit", feature = "visit-mut"))] -pub use crate::gen::*; +pub use crate::r#gen::*; // Not public API. #[doc(hidden)] diff --git a/src/parse.rs b/src/parse.rs index 50fe110b58..ec39fe16d0 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -645,6 +645,26 @@ impl<'a> ParseBuffer<'a> { peek3(self, T::Token::peek) } + /// Looks at the fourth-next token in the parse stream. + pub fn peek4(&self, token: T) -> bool { + fn peek4(buffer: &ParseBuffer, peek: fn(Cursor) -> bool) -> bool { + if let Some(group) = buffer.cursor().group(Delimiter::None) { + if group.0.skip().and_then(Cursor::skip).and_then(Cursor::skip).map_or(false, peek) { + return true; + } + } + buffer + .cursor() + .skip() + .and_then(Cursor::skip) + .and_then(Cursor::skip) + .map_or(false, peek) + } + + let _ = token; + peek4(self, T::Token::peek) + } + /// Parses zero or more occurrences of `T` separated by punctuation of type /// `P`, with optional trailing punctuation. /// diff --git a/src/stmt.rs b/src/stmt.rs index fd14aec26c..1dbebef0b8 100644 --- a/src/stmt.rs +++ b/src/stmt.rs @@ -224,7 +224,10 @@ pub(crate) mod parsing { || input.peek(Token![async]) && (input.peek2(Token![unsafe]) || input.peek2(Token![extern]) - || input.peek2(Token![fn])) + || input.peek2(Token![fn]) + || (input.peek2(Token![gen]) && input.peek3(Token![fn]))) + || input.peek(Token![gen]) + && input.peek2(Token![fn]) || input.peek(Token![fn]) || input.peek(Token![mod]) || input.peek(Token![type]) @@ -338,6 +341,7 @@ pub(crate) mod parsing { | Expr::Continue(_) | Expr::Field(_) | Expr::ForLoop(_) + | Expr::Gen(_) | Expr::Group(_) | Expr::If(_) | Expr::Index(_) diff --git a/src/token.rs b/src/token.rs index 05d8f5601b..1db6e3a483 100644 --- a/src/token.rs +++ b/src/token.rs @@ -754,6 +754,7 @@ define_keywords! { "final" pub struct Final "fn" pub struct Fn "for" pub struct For + "gen" pub struct Gen "if" pub struct If "impl" pub struct Impl "in" pub struct In @@ -933,6 +934,7 @@ macro_rules! Token { [fn] => { $crate::token::Fn }; [for] => { $crate::token::For }; [if] => { $crate::token::If }; + [gen] => { $crate::token::Gen }; [impl] => { $crate::token::Impl }; [in] => { $crate::token::In }; [let] => { $crate::token::Let }; diff --git a/syn.json b/syn.json index 7c5c12149e..23a3fd5c0c 100644 --- a/syn.json +++ b/syn.json @@ -710,6 +710,11 @@ "syn": "ExprForLoop" } ], + "Gen": [ + { + "syn": "ExprGen" + } + ], "Group": [ { "syn": "ExprGroup" @@ -1259,6 +1264,37 @@ } } }, + { + "ident": "ExprGen", + "features": { + "any": [ + "full" + ] + }, + "fields": { + "attrs": { + "vec": { + "syn": "Attribute" + } + }, + "async_token": { + "option": { + "token": "Async" + } + }, + "gen_token": { + "token": "Gen" + }, + "capture": { + "option": { + "token": "Move" + } + }, + "block": { + "syn": "Block" + } + } + }, { "ident": "ExprGroup", "features": { @@ -4328,6 +4364,11 @@ "token": "Async" } }, + "generator": { + "option": { + "token": "Gen" + } + }, "unsafety": { "option": { "token": "Unsafe" @@ -5496,6 +5537,7 @@ "Fn": "fn", "For": "for", "Ge": ">=", + "Gen": "gen", "Gt": ">", "If": "if", "Impl": "impl", diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs index 95ac837157..22730c7540 100644 --- a/tests/debug/gen.rs +++ b/tests/debug/gen.rs @@ -659,6 +659,20 @@ impl Debug for Lite { formatter.field("body", Lite(&_val.body)); formatter.finish() } + syn::Expr::Gen(_val) => { + let mut formatter = formatter.debug_struct("Expr::Gen"); + if !_val.attrs.is_empty() { + formatter.field("attrs", Lite(&_val.attrs)); + } + if _val.async_token.is_some() { + formatter.field("async_token", &Present); + } + if _val.capture.is_some() { + formatter.field("capture", &Present); + } + formatter.field("block", Lite(&_val.block)); + formatter.finish() + } syn::Expr::Group(_val) => { let mut formatter = formatter.debug_struct("Expr::Group"); if !_val.attrs.is_empty() { @@ -1290,6 +1304,22 @@ impl Debug for Lite { formatter.finish() } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = formatter.debug_struct("ExprGen"); + if !self.value.attrs.is_empty() { + formatter.field("attrs", Lite(&self.value.attrs)); + } + if self.value.async_token.is_some() { + formatter.field("async_token", &Present); + } + if self.value.capture.is_some() { + formatter.field("capture", &Present); + } + formatter.field("block", Lite(&self.value.block)); + formatter.finish() + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut formatter = formatter.debug_struct("ExprGroup"); @@ -3660,6 +3690,9 @@ impl Debug for Lite { if self.value.asyncness.is_some() { formatter.field("asyncness", &Present); } + if self.value.generator.is_some() { + formatter.field("generator", &Present); + } if self.value.unsafety.is_some() { formatter.field("unsafety", &Present); } diff --git a/tests/debug/mod.rs b/tests/debug/mod.rs index 707de0e2f0..5d3dcdc877 100644 --- a/tests/debug/mod.rs +++ b/tests/debug/mod.rs @@ -5,7 +5,7 @@ )] #[rustfmt::skip] -mod gen; +mod r#gen; use proc_macro2::{Ident, Literal, TokenStream}; use ref_cast::RefCast; diff --git a/tests/test_gen.rs b/tests/test_gen.rs new file mode 100644 index 0000000000..668271f5cb --- /dev/null +++ b/tests/test_gen.rs @@ -0,0 +1,124 @@ +#![allow(clippy::uninlined_format_args)] + +#[macro_use] +mod macros; + +use syn::{Expr, Item}; + +#[test] +fn test_gen_block() { + let input = "gen { }"; + + snapshot!(input as Expr, @r###" + Expr::Gen { + block: Block { + stmts: [], + }, + } + "###); +} + +#[test] +fn test_gen_move_block() { + let input = "gen move { }"; + + snapshot!(input as Expr, @r###" + Expr::Gen { + capture: Some, + block: Block { + stmts: [], + }, + } + "###); +} + +#[test] +fn test_async_gen_block() { + let input = "async gen { }"; + + snapshot!(input as Expr, @r###" + Expr::Gen { + async_token: Some, + block: Block { + stmts: [], + }, + } + "###); +} + +#[test] +fn test_async_gen_move_block() { + let input = "async gen move { }"; + + snapshot!(input as Expr, @r###" + Expr::Gen { + async_token: Some, + capture: Some, + block: Block { + stmts: [], + }, + } + "###); +} + +#[test] +fn test_gen_fn() { + let input = "gen fn foo() -> i32 { }"; + + snapshot!(input as Item, @r###" + Item::Fn { + vis: Visibility::Inherited, + sig: Signature { + generator: Some, + ident: "foo", + generics: Generics, + output: ReturnType::Type( + Type::Path { + path: Path { + segments: [ + PathSegment { + ident: "i32", + }, + ], + }, + }, + ), + }, + block: Block { + stmts: [], + }, + } + "###); +} + + +#[test] +fn test_async_gen_fn() { + let input = "async gen fn foo() -> i32 { }"; + + snapshot!(input as Item, @r###" + Item::Fn { + vis: Visibility::Inherited, + sig: Signature { + asyncness: Some, + generator: Some, + ident: "foo", + generics: Generics, + output: ReturnType::Type( + Type::Path { + path: Path { + segments: [ + PathSegment { + ident: "i32", + }, + ], + }, + }, + ), + }, + block: Block { + stmts: [], + }, + } + "###); +} diff --git a/tests/test_size.rs b/tests/test_size.rs index 943fcd354b..69e700fa13 100644 --- a/tests/test_size.rs +++ b/tests/test_size.rs @@ -14,7 +14,7 @@ fn test_expr_size() { #[rustversion::attr(before(2022-09-09), ignore)] #[test] fn test_item_size() { - assert_eq!(mem::size_of::(), 360); + assert_eq!(mem::size_of::(), 368); } #[rustversion::attr(before(2023-04-29), ignore)]