| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- // SPDX-License-Identifier: Apache-2.0 OR MIT
- use crate::attr::Attribute;
- use crate::expr::Expr;
- use crate::item::Item;
- use crate::mac::Macro;
- use crate::pat::Pat;
- use crate::token;
- ast_struct! {
- /// A braced block containing Rust statements.
- #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
- pub struct Block {
- pub brace_token: token::Brace,
- /// Statements in a block
- pub stmts: Vec<Stmt>,
- }
- }
- ast_enum! {
- /// A statement, usually ending in a semicolon.
- #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
- pub enum Stmt {
- /// A local (let) binding.
- Local(Local),
- /// An item definition.
- Item(Item),
- /// Expression, with or without trailing semicolon.
- Expr(Expr, Option<Token![;]>),
- /// A macro invocation in statement position.
- ///
- /// Syntactically it's ambiguous which other kind of statement this
- /// macro would expand to. It can be any of local variable (`let`),
- /// item, or expression.
- Macro(StmtMacro),
- }
- }
- ast_struct! {
- /// A local `let` binding: `let x: u64 = s.parse()?;`.
- #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
- pub struct Local {
- pub attrs: Vec<Attribute>,
- pub let_token: Token![let],
- pub pat: Pat,
- pub init: Option<LocalInit>,
- pub semi_token: Token![;],
- }
- }
- ast_struct! {
- /// The expression assigned in a local `let` binding, including optional
- /// diverging `else` block.
- ///
- /// `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and
- /// `= r else { return }` in `let Ok(x) = r else { return }`.
- #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
- pub struct LocalInit {
- pub eq_token: Token![=],
- pub expr: Box<Expr>,
- pub diverge: Option<(Token![else], Box<Expr>)>,
- }
- }
- ast_struct! {
- /// A macro invocation in statement position.
- ///
- /// Syntactically it's ambiguous which other kind of statement this macro
- /// would expand to. It can be any of local variable (`let`), item, or
- /// expression.
- #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
- pub struct StmtMacro {
- pub attrs: Vec<Attribute>,
- pub mac: Macro,
- pub semi_token: Option<Token![;]>,
- }
- }
- #[cfg(feature = "parsing")]
- pub(crate) mod parsing {
- use crate::attr::Attribute;
- use crate::classify;
- use crate::error::Result;
- use crate::expr::{Expr, ExprBlock, ExprMacro};
- use crate::ident::Ident;
- use crate::item;
- use crate::mac::{self, Macro};
- use crate::parse::discouraged::Speculative as _;
- use crate::parse::{Parse, ParseStream};
- use crate::pat::{Pat, PatType};
- use crate::path::Path;
- use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
- use crate::token;
- use crate::ty::Type;
- use proc_macro2::TokenStream;
- struct AllowNoSemi(bool);
- impl Block {
- /// Parse the body of a block as zero or more statements, possibly
- /// including one trailing expression.
- ///
- /// # 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<Attribute>,
- /// fn_token: Token![fn],
- /// name: Ident,
- /// brace_token: token::Brace,
- /// stmts: Vec<Stmt>,
- /// }
- ///
- /// impl Parse for MiniFunction {
- /// fn parse(input: ParseStream) -> Result<Self> {
- /// 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(docsrs, doc(cfg(feature = "parsing")))]
- pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
- let mut stmts = Vec::new();
- loop {
- while let semi @ Some(_) = input.parse()? {
- stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
- }
- if input.is_empty() {
- break;
- }
- let stmt = parse_stmt(input, AllowNoSemi(true))?;
- let requires_semicolon = match &stmt {
- Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
- Stmt::Macro(stmt) => {
- stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
- }
- Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
- };
- stmts.push(stmt);
- if input.is_empty() {
- break;
- } else if requires_semicolon {
- return Err(input.error("unexpected token, expected `;`"));
- }
- }
- Ok(stmts)
- }
- }
- #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
- impl Parse for Block {
- fn parse(input: ParseStream) -> Result<Self> {
- let content;
- Ok(Block {
- brace_token: braced!(content in input),
- stmts: content.call(Block::parse_within)?,
- })
- }
- }
- #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
- impl Parse for Stmt {
- fn parse(input: ParseStream) -> Result<Self> {
- let allow_nosemi = AllowNoSemi(false);
- parse_stmt(input, allow_nosemi)
- }
- }
- fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
- let begin = input.fork();
- let attrs = input.call(Attribute::parse_outer)?;
- // brace-style macros; paren and bracket macros get parsed as
- // expression statements.
- let ahead = input.fork();
- let mut is_item_macro = false;
- if let Ok(path) = ahead.call(Path::parse_mod_style) {
- if ahead.peek(Token![!]) {
- if ahead.peek2(Ident) || ahead.peek2(Token![try]) {
- is_item_macro = true;
- } else if ahead.peek2(token::Brace)
- && !(ahead.peek3(Token![.]) && !ahead.peek3(Token![..])
- || ahead.peek3(Token![?]))
- {
- input.advance_to(&ahead);
- return stmt_mac(input, attrs, path).map(Stmt::Macro);
- }
- }
- }
- if input.peek(Token![let]) && !input.peek(token::Group) {
- stmt_local(input, attrs).map(Stmt::Local)
- } 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.peek2(Token![static])
- || input.peek2(Token![async])
- && !(input.peek3(Token![unsafe])
- || input.peek3(Token![extern])
- || input.peek3(Token![fn]))
- || input.peek2(Token![move])
- || input.peek2(Token![|]))
- || 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])
- || is_item_macro
- {
- let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
- Ok(Stmt::Item(item))
- } else {
- stmt_expr(input, allow_nosemi, attrs)
- }
- }
- fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
- let bang_token: Token![!] = input.parse()?;
- let (delimiter, tokens) = mac::parse_delimiter(input)?;
- let semi_token: Option<Token![;]> = input.parse()?;
- Ok(StmtMacro {
- attrs,
- mac: Macro {
- path,
- bang_token,
- delimiter,
- tokens,
- },
- semi_token,
- })
- }
- fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
- let let_token: Token![let] = input.parse()?;
- let mut pat = Pat::parse_single(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 let Some(eq_token) = input.parse()? {
- let eq_token: Token![=] = eq_token;
- let expr: Expr = input.parse()?;
- let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(Token![else]) {
- let else_token: Token![else] = input.parse()?;
- let diverge = ExprBlock {
- attrs: Vec::new(),
- label: None,
- block: input.parse()?,
- };
- Some((else_token, Box::new(Expr::Block(diverge))))
- } else {
- None
- };
- Some(LocalInit {
- eq_token,
- expr: Box::new(expr),
- diverge,
- })
- } else {
- None
- };
- let semi_token: Token![;] = input.parse()?;
- Ok(Local {
- attrs,
- let_token,
- pat,
- init,
- semi_token,
- })
- }
- fn stmt_expr(
- input: ParseStream,
- allow_nosemi: AllowNoSemi,
- mut attrs: Vec<Attribute>,
- ) -> Result<Stmt> {
- let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
- let mut attr_target = &mut e;
- loop {
- attr_target = match attr_target {
- Expr::Assign(e) => &mut e.left,
- Expr::Binary(e) => &mut e.left,
- Expr::Cast(e) => &mut e.expr,
- Expr::Array(_)
- | Expr::Async(_)
- | Expr::Await(_)
- | Expr::Block(_)
- | Expr::Break(_)
- | Expr::Call(_)
- | Expr::Closure(_)
- | Expr::Const(_)
- | Expr::Continue(_)
- | Expr::Field(_)
- | Expr::ForLoop(_)
- | Expr::Group(_)
- | Expr::If(_)
- | Expr::Index(_)
- | Expr::Infer(_)
- | Expr::Let(_)
- | Expr::Lit(_)
- | Expr::Loop(_)
- | Expr::Macro(_)
- | Expr::Match(_)
- | Expr::MethodCall(_)
- | Expr::Paren(_)
- | Expr::Path(_)
- | Expr::Range(_)
- | Expr::RawAddr(_)
- | Expr::Reference(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Struct(_)
- | Expr::Try(_)
- | Expr::TryBlock(_)
- | Expr::Tuple(_)
- | Expr::Unary(_)
- | Expr::Unsafe(_)
- | Expr::While(_)
- | Expr::Yield(_)
- | Expr::Verbatim(_) => break,
- };
- }
- attrs.extend(attr_target.replace_attrs(Vec::new()));
- attr_target.replace_attrs(attrs);
- let semi_token: Option<Token![;]> = input.parse()?;
- match e {
- Expr::Macro(ExprMacro { attrs, mac })
- if semi_token.is_some() || mac.delimiter.is_brace() =>
- {
- return Ok(Stmt::Macro(StmtMacro {
- attrs,
- mac,
- semi_token,
- }));
- }
- _ => {}
- }
- if semi_token.is_some() {
- Ok(Stmt::Expr(e, semi_token))
- } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
- Ok(Stmt::Expr(e, None))
- } else {
- Err(input.error("expected semicolon"))
- }
- }
- }
- #[cfg(feature = "printing")]
- pub(crate) mod printing {
- use crate::classify;
- use crate::expr::{self, Expr};
- use crate::fixup::FixupContext;
- use crate::stmt::{Block, Local, Stmt, StmtMacro};
- use crate::token;
- use proc_macro2::TokenStream;
- use quote::{ToTokens, TokenStreamExt};
- #[cfg_attr(docsrs, 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(docsrs, 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, semi) => {
- expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
- semi.to_tokens(tokens);
- }
- Stmt::Macro(mac) => mac.to_tokens(tokens),
- }
- }
- }
- #[cfg_attr(docsrs, 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(init) = &self.init {
- init.eq_token.to_tokens(tokens);
- expr::printing::print_subexpression(
- &init.expr,
- init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
- tokens,
- FixupContext::NONE,
- );
- if let Some((else_token, diverge)) = &init.diverge {
- else_token.to_tokens(tokens);
- match &**diverge {
- Expr::Block(diverge) => diverge.to_tokens(tokens),
- _ => token::Brace::default().surround(tokens, |tokens| {
- expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
- }),
- }
- }
- }
- self.semi_token.to_tokens(tokens);
- }
- }
- #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
- impl ToTokens for StmtMacro {
- fn to_tokens(&self, tokens: &mut TokenStream) {
- expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
- self.mac.to_tokens(tokens);
- self.semi_token.to_tokens(tokens);
- }
- }
- }
|