use super::*; ast_struct! { /// A braced block containing Rust statements. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct Block { pub brace_token: token::Brace, /// Statements in a block pub stmts: Vec, } } ast_enum! { /// A statement, usually ending in a semicolon. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub enum Stmt { /// A local (let) binding. Local(Local), /// An item definition. Item(Item), /// Expr without trailing semicolon. Expr(Expr), /// Expression with trailing semicolon. Semi(Expr, Token![;]), } } ast_struct! { /// A local `let` binding: `let x: u64 = s.parse()?`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct Local { pub attrs: Vec, pub let_token: Token![let], pub pat: Pat, pub init: Option<(Token![=], Box)>, pub semi_token: Token![;], } } #[cfg(feature = "parsing")] pub mod parsing { use super::*; use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; use proc_macro2::TokenStream; impl Block { /// Parse the body of a block as zero or more statements, possibly /// including one trailing expression. /// /// *This function is available only if Syn is built with the `"parsing"` /// feature.* /// /// # Example /// /// ``` /// use syn::{braced, token, Attribute, Block, Ident, Result, Stmt, Token}; /// use syn::parse::{Parse, ParseStream}; /// /// // Parse a function with no generics or parameter list. /// // /// // fn playground { /// // let mut x = 1; /// // x += 1; /// // println!("{}", x); /// // } /// struct MiniFunction { /// attrs: Vec, /// fn_token: Token![fn], /// name: Ident, /// brace_token: token::Brace, /// stmts: Vec, /// } /// /// impl Parse for MiniFunction { /// fn parse(input: ParseStream) -> Result { /// let outer_attrs = input.call(Attribute::parse_outer)?; /// let fn_token: Token![fn] = input.parse()?; /// let name: Ident = input.parse()?; /// /// let content; /// let brace_token = braced!(content in input); /// let inner_attrs = content.call(Attribute::parse_inner)?; /// let stmts = content.call(Block::parse_within)?; /// /// Ok(MiniFunction { /// attrs: { /// let mut attrs = outer_attrs; /// attrs.extend(inner_attrs); /// attrs /// }, /// fn_token, /// name, /// brace_token, /// stmts, /// }) /// } /// } /// ``` #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] pub fn parse_within(input: ParseStream) -> Result> { let mut stmts = Vec::new(); loop { while let Some(semi) = input.parse::>()? { stmts.push(Stmt::Semi(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 { expr::requires_terminator(s) } else { false }; stmts.push(s); if input.is_empty() { break; } else if requires_semicolon { return Err(input.error("unexpected token")); } } Ok(stmts) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Block { fn parse(input: ParseStream) -> Result { let content; Ok(Block { brace_token: braced!(content in input), stmts: content.call(Block::parse_within)?, }) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Stmt { fn parse(input: ParseStream) -> Result { parse_stmt(input, false) } } fn parse_stmt(input: ParseStream, allow_nosemi: bool) -> Result { let begin = input.fork(); let mut attrs = input.call(Attribute::parse_outer)?; // brace-style macros; paren and bracket macros get parsed as // expression statements. let ahead = input.fork(); if let Ok(path) = ahead.call(Path::parse_mod_style) { if ahead.peek(Token![!]) && (ahead.peek2(token::Brace) && !(ahead.peek3(Token![.]) || ahead.peek3(Token![?])) || ahead.peek2(Ident)) { input.advance_to(&ahead); return stmt_mac(input, attrs, path); } } if input.peek(Token![let]) { stmt_local(input, attrs, begin) } else if input.peek(Token![pub]) || input.peek(Token![crate]) && !input.peek2(Token![::]) || input.peek(Token![extern]) || input.peek(Token![use]) || input.peek(Token![static]) && (input.peek2(Token![mut]) || input.peek2(Ident) && !(input.peek2(Token![async]) && (input.peek3(Token![move]) || input.peek3(Token![|])))) || input.peek(Token![const]) && !input.peek2(token::Brace) || input.peek(Token![unsafe]) && !input.peek2(token::Brace) || input.peek(Token![async]) && (input.peek2(Token![unsafe]) || input.peek2(Token![extern]) || input.peek2(Token![fn])) || input.peek(Token![fn]) || input.peek(Token![mod]) || input.peek(Token![type]) || input.peek(Token![struct]) || input.peek(Token![enum]) || input.peek(Token![union]) && input.peek2(Ident) || input.peek(Token![auto]) && input.peek2(Token![trait]) || input.peek(Token![trait]) || input.peek(Token![default]) && (input.peek2(Token![unsafe]) || input.peek2(Token![impl])) || input.peek(Token![impl]) || input.peek(Token![macro]) { let mut item: Item = input.parse()?; attrs.extend(item.replace_attrs(Vec::new())); item.replace_attrs(attrs); Ok(Stmt::Item(item)) } else { stmt_expr(input, allow_nosemi, attrs) } } fn stmt_mac(input: ParseStream, attrs: Vec, path: Path) -> Result { let bang_token: Token![!] = input.parse()?; let ident: Option = input.parse()?; let (delimiter, tokens) = mac::parse_delimiter(input)?; let semi_token: Option = input.parse()?; Ok(Stmt::Item(Item::Macro(ItemMacro { attrs, ident, mac: Macro { path, bang_token, delimiter, tokens, }, semi_token, }))) } fn stmt_local(input: ParseStream, attrs: Vec, begin: ParseBuffer) -> Result { let let_token: Token![let] = input.parse()?; let mut pat: Pat = pat::parsing::multi_pat_with_leading_vert(input)?; if input.peek(Token![:]) { let colon_token: Token![:] = input.parse()?; let ty: Type = input.parse()?; pat = Pat::Type(PatType { attrs: Vec::new(), pat: Box::new(pat), colon_token, ty: Box::new(ty), }); } let init = if input.peek(Token![=]) { let eq_token: Token![=] = input.parse()?; let init: Expr = input.parse()?; if input.peek(Token![else]) { input.parse::()?; let content; braced!(content in input); 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)); } Some((eq_token, Box::new(init))) } else { None }; let semi_token: Token![;] = input.parse()?; Ok(Stmt::Local(Local { attrs, let_token, pat, init, semi_token, })) } fn stmt_expr( input: ParseStream, allow_nosemi: bool, mut attrs: Vec, ) -> Result { let mut e = expr::parsing::expr_early(input)?; let mut attr_target = &mut e; loop { attr_target = match attr_target { Expr::Assign(e) => &mut e.left, Expr::AssignOp(e) => &mut e.left, Expr::Binary(e) => &mut e.left, _ => break, }; } 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 allow_nosemi || !expr::requires_terminator(&e) { Ok(Stmt::Expr(e)) } else { Err(input.error("expected semicolon")) } } } #[cfg(feature = "printing")] mod printing { use super::*; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Block { fn to_tokens(&self, tokens: &mut TokenStream) { self.brace_token.surround(tokens, |tokens| { tokens.append_all(&self.stmts); }); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Stmt { fn to_tokens(&self, tokens: &mut TokenStream) { 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) => { expr.to_tokens(tokens); semi.to_tokens(tokens); } } } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for Local { fn to_tokens(&self, tokens: &mut TokenStream) { expr::printing::outer_attrs_to_tokens(&self.attrs, tokens); self.let_token.to_tokens(tokens); self.pat.to_tokens(tokens); if let Some((eq_token, init)) = &self.init { eq_token.to_tokens(tokens); init.to_tokens(tokens); } self.semi_token.to_tokens(tokens); } } }