Skip to main content

syn/
stmt.rs

1use crate::attr::Attribute;
2use crate::expr::Expr;
3use crate::item::Item;
4use crate::mac::Macro;
5use crate::pat::Pat;
6use crate::token;
7use alloc::boxed::Box;
8use alloc::vec::Vec;
9
10#[doc = r" A braced block containing Rust statements."]
pub struct Block {
    pub brace_token: token::Brace,
    #[doc = r" Statements in a block"]
    pub stmts: Vec<Stmt>,
}ast_struct! {
11    /// A braced block containing Rust statements.
12    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
13    pub struct Block {
14        pub brace_token: token::Brace,
15        /// Statements in a block
16        pub stmts: Vec<Stmt>,
17    }
18}
19
20#[doc = r" A statement, usually ending in a semicolon."]
pub enum Stmt {

    #[doc = r" A local (let) binding."]
    Local(Local),

    #[doc = r" An item definition."]
    Item(Item),

    #[doc = r" Expression, with or without trailing semicolon."]
    Expr(Expr, Option<crate::token::Semi>),

    #[doc = r" A macro invocation in statement position."]
    #[doc = r""]
    #[doc =
    r" Syntactically it's ambiguous which other kind of statement this"]
    #[doc =
    r" macro would expand to. It can be any of local variable (`let`),"]
    #[doc = r" item, or expression."]
    Macro(StmtMacro),
}ast_enum! {
21    /// A statement, usually ending in a semicolon.
22    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
23    pub enum Stmt {
24        /// A local (let) binding.
25        Local(Local),
26
27        /// An item definition.
28        Item(Item),
29
30        /// Expression, with or without trailing semicolon.
31        Expr(Expr, Option<Token![;]>),
32
33        /// A macro invocation in statement position.
34        ///
35        /// Syntactically it's ambiguous which other kind of statement this
36        /// macro would expand to. It can be any of local variable (`let`),
37        /// item, or expression.
38        Macro(StmtMacro),
39    }
40}
41
42#[doc = r" A local `let` binding: `let x: u64 = s.parse()?;`."]
pub struct Local {
    pub attrs: Vec<Attribute>,
    pub let_token: crate::token::Let,
    pub pat: Pat,
    pub init: Option<LocalInit>,
    pub semi_token: crate::token::Semi,
}ast_struct! {
43    /// A local `let` binding: `let x: u64 = s.parse()?;`.
44    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
45    pub struct Local {
46        pub attrs: Vec<Attribute>,
47        pub let_token: Token![let],
48        pub pat: Pat,
49        pub init: Option<LocalInit>,
50        pub semi_token: Token![;],
51    }
52}
53
54#[doc =
r" The expression assigned in a local `let` binding, including optional"]
#[doc = r" diverging `else` block."]
#[doc = r""]
#[doc =
r" `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and"]
#[doc = r" `= r else { return }` in `let Ok(x) = r else { return }`."]
pub struct LocalInit {
    pub eq_token: crate::token::Eq,
    pub expr: Box<Expr>,
    pub diverge: Option<(crate::token::Else, Box<Expr>)>,
}ast_struct! {
55    /// The expression assigned in a local `let` binding, including optional
56    /// diverging `else` block.
57    ///
58    /// `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and
59    /// `= r else { return }` in `let Ok(x) = r else { return }`.
60    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
61    pub struct LocalInit {
62        pub eq_token: Token![=],
63        pub expr: Box<Expr>,
64        pub diverge: Option<(Token![else], Box<Expr>)>,
65    }
66}
67
68#[doc = r" A macro invocation in statement position."]
#[doc = r""]
#[doc =
r" Syntactically it's ambiguous which other kind of statement this macro"]
#[doc =
r" would expand to. It can be any of local variable (`let`), item, or"]
#[doc = r" expression."]
pub struct StmtMacro {
    pub attrs: Vec<Attribute>,
    pub mac: Macro,
    pub semi_token: Option<crate::token::Semi>,
}ast_struct! {
69    /// A macro invocation in statement position.
70    ///
71    /// Syntactically it's ambiguous which other kind of statement this macro
72    /// would expand to. It can be any of local variable (`let`), item, or
73    /// expression.
74    #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
75    pub struct StmtMacro {
76        pub attrs: Vec<Attribute>,
77        pub mac: Macro,
78        pub semi_token: Option<Token![;]>,
79    }
80}
81
82#[cfg(feature = "parsing")]
83pub(crate) mod parsing {
84    use crate::attr::Attribute;
85    use crate::buffer::Cursor;
86    use crate::classify;
87    use crate::error::Result;
88    use crate::expr::{Expr, ExprBlock, ExprMacro};
89    use crate::ident::Ident;
90    use crate::item;
91    use crate::mac::{self, Macro};
92    use crate::parse::discouraged::Speculative as _;
93    use crate::parse::{Parse, ParseStream};
94    use crate::pat::{Pat, PatType};
95    use crate::path::Path;
96    use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
97    use crate::token;
98    use crate::ty::Type;
99    use crate::verbatim;
100    use alloc::boxed::Box;
101    use alloc::vec::Vec;
102    use core::mem;
103    use proc_macro2::TokenStream;
104
105    struct AllowNoSemi(bool);
106
107    impl Block {
108        /// Parse the body of a block as zero or more statements, possibly
109        /// including one trailing expression.
110        ///
111        /// # Example
112        ///
113        /// ```
114        /// use syn::{braced, token, Attribute, Block, Ident, Result, Stmt, Token};
115        /// use syn::parse::{Parse, ParseStream};
116        ///
117        /// // Parse a function with no generics or parameter list.
118        /// //
119        /// //     fn playground {
120        /// //         let mut x = 1;
121        /// //         x += 1;
122        /// //         println!("{}", x);
123        /// //     }
124        /// struct MiniFunction {
125        ///     attrs: Vec<Attribute>,
126        ///     fn_token: Token![fn],
127        ///     name: Ident,
128        ///     brace_token: token::Brace,
129        ///     stmts: Vec<Stmt>,
130        /// }
131        ///
132        /// impl Parse for MiniFunction {
133        ///     fn parse(input: ParseStream) -> Result<Self> {
134        ///         let outer_attrs = input.call(Attribute::parse_outer)?;
135        ///         let fn_token: Token![fn] = input.parse()?;
136        ///         let name: Ident = input.parse()?;
137        ///
138        ///         let content;
139        ///         let brace_token = braced!(content in input);
140        ///         let inner_attrs = content.call(Attribute::parse_inner)?;
141        ///         let stmts = content.call(Block::parse_within)?;
142        ///
143        ///         Ok(MiniFunction {
144        ///             attrs: {
145        ///                 let mut attrs = outer_attrs;
146        ///                 attrs.extend(inner_attrs);
147        ///                 attrs
148        ///             },
149        ///             fn_token,
150        ///             name,
151        ///             brace_token,
152        ///             stmts,
153        ///         })
154        ///     }
155        /// }
156        /// ```
157        #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
158        pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
159            let mut stmts = Vec::new();
160            loop {
161                while let semi @ Some(_) = input.parse()? {
162                    stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
163                }
164                if input.is_empty() {
165                    break;
166                }
167                let stmt = parse_stmt(input, AllowNoSemi(true))?;
168                let requires_semicolon = match &stmt {
169                    Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
170                    Stmt::Macro(stmt) => {
171                        stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
172                    }
173                    Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
174                };
175                stmts.push(stmt);
176                if input.is_empty() {
177                    break;
178                } else if requires_semicolon {
179                    return Err(input.error("unexpected token, expected `;`"));
180                }
181            }
182            Ok(stmts)
183        }
184    }
185
186    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
187    impl Parse for Block {
188        fn parse(input: ParseStream) -> Result<Self> {
189            let content;
190            Ok(Block {
191                brace_token: match crate::__private::parse_braces(&input) {
    crate::__private::Ok(braces) => {
        content = braces.content;
        _ = content;
        braces.token
    }
    crate::__private::Err(error) => { return crate::__private::Err(error); }
}braced!(content in input),
192                stmts: content.call(Block::parse_within)?,
193            })
194        }
195    }
196
197    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
198    impl Parse for Stmt {
199        fn parse(input: ParseStream) -> Result<Self> {
200            let allow_nosemi = AllowNoSemi(false);
201            parse_stmt(input, allow_nosemi)
202        }
203    }
204
205    fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
206        let begin = input.cursor();
207        let attrs = input.call(Attribute::parse_outer)?;
208        let attrs_end = input.cursor();
209
210        // brace-style macros; paren and bracket macros get parsed as
211        // expression statements.
212        let ahead = input.fork();
213        let mut is_item_macro = false;
214        if let Ok(path) = ahead.call(Path::parse_mod_style) {
215            if ahead.peek(crate::token::NotToken![!]) {
216                if ahead.peek2(Ident) || ahead.peek2(crate::token::TryToken![try]) {
217                    is_item_macro = true;
218                } else if ahead.peek2(token::Brace)
219                    && !(ahead.peek3(crate::token::DotToken![.]) && !ahead.peek3(crate::token::DotDotToken![..])
220                        || ahead.peek3(crate::token::QuestionToken![?]))
221                {
222                    input.advance_to(&ahead);
223                    return stmt_mac(input, attrs, path).map(Stmt::Macro);
224                }
225            }
226        }
227
228        if input.peek(crate::token::LetToken![let]) && !input.peek(token::Group) {
229            stmt_local(input, attrs).map(Stmt::Local)
230        } else if input.peek(crate::token::PubToken![pub])
231            || input.peek(crate::token::CrateToken![crate]) && !input.peek2(crate::token::PathSepToken![::])
232            || input.peek(crate::token::ExternToken![extern])
233            || input.peek(crate::token::UseToken![use])
234            || input.peek(crate::token::StaticToken![static])
235                && (input.peek2(crate::token::MutToken![mut])
236                    || input.peek2(Ident)
237                        && !(input.peek2(crate::token::AsyncToken![async])
238                            && (input.peek3(crate::token::MoveToken![move]) || input.peek3(crate::token::OrToken![|]))))
239            || input.peek(crate::token::ConstToken![const])
240                && !(input.peek2(token::Brace)
241                    || input.peek2(crate::token::StaticToken![static])
242                    || input.peek2(crate::token::AsyncToken![async])
243                        && !(input.peek3(crate::token::UnsafeToken![unsafe])
244                            || input.peek3(crate::token::ExternToken![extern])
245                            || input.peek3(crate::token::FnToken![fn]))
246                    || input.peek2(crate::token::MoveToken![move])
247                    || input.peek2(crate::token::OrToken![|]))
248            || input.peek(crate::token::UnsafeToken![unsafe]) && !input.peek2(token::Brace)
249            || input.peek(crate::token::AsyncToken![async])
250                && (input.peek2(crate::token::UnsafeToken![unsafe])
251                    || input.peek2(crate::token::ExternToken![extern])
252                    || input.peek2(crate::token::FnToken![fn]))
253            || input.peek(crate::token::FnToken![fn])
254            || input.peek(crate::token::ModToken![mod])
255            || input.peek(crate::token::TypeToken![type])
256            || input.peek(crate::token::StructToken![struct])
257            || input.peek(crate::token::EnumToken![enum])
258            || input.peek(crate::token::UnionToken![union]) && input.peek2(Ident)
259            || input.peek(crate::token::AutoToken![auto]) && input.peek2(crate::token::TraitToken![trait])
260            || input.peek(crate::token::TraitToken![trait])
261            || input.peek(crate::token::DefaultToken![default])
262                && (input.peek2(crate::token::UnsafeToken![unsafe]) || input.peek2(crate::token::ImplToken![impl]))
263            || input.peek(crate::token::ImplToken![impl])
264            || input.peek(crate::token::MacroToken![macro])
265            || is_item_macro
266        {
267            let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
268            Ok(Stmt::Item(item))
269        } else {
270            stmt_expr(begin, input, allow_nosemi, attrs, attrs_end)
271        }
272    }
273
274    fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
275        let bang_token: crate::token::NotToken![!] = input.parse()?;
276        let (delimiter, tokens) = mac::parse_delimiter(input)?;
277        let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
278
279        Ok(StmtMacro {
280            attrs,
281            mac: Macro {
282                path,
283                bang_token,
284                delimiter,
285                tokens,
286            },
287            semi_token,
288        })
289    }
290
291    fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
292        let let_token: crate::token::LetToken![let] = input.parse()?;
293
294        let mut pat = Pat::parse_single(input)?;
295        if input.peek(crate::token::ColonToken![:]) {
296            let colon_token: crate::token::ColonToken![:] = input.parse()?;
297            let ty: Type = input.parse()?;
298            pat = Pat::Type(PatType {
299                attrs: Vec::new(),
300                pat: Box::new(pat),
301                colon_token,
302                ty: Box::new(ty),
303            });
304        }
305
306        let init = if let Some(eq_token) = input.parse()? {
307            let eq_token: crate::token::EqToken![=] = eq_token;
308            let expr: Expr = input.parse()?;
309
310            let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(crate::token::ElseToken![else]) {
311                let else_token: crate::token::ElseToken![else] = input.parse()?;
312                let diverge = ExprBlock {
313                    attrs: Vec::new(),
314                    label: None,
315                    block: input.parse()?,
316                };
317                Some((else_token, Box::new(Expr::Block(diverge))))
318            } else {
319                None
320            };
321
322            Some(LocalInit {
323                eq_token,
324                expr: Box::new(expr),
325                diverge,
326            })
327        } else {
328            None
329        };
330
331        let semi_token: crate::token::SemiToken![;] = input.parse()?;
332
333        Ok(Local {
334            attrs,
335            let_token,
336            pat,
337            init,
338            semi_token,
339        })
340    }
341
342    fn stmt_expr(
343        begin: Cursor,
344        input: ParseStream,
345        allow_nosemi: AllowNoSemi,
346        mut attrs: Vec<Attribute>,
347        attrs_end: Cursor,
348    ) -> Result<Stmt> {
349        let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
350
351        let mut attr_target = &mut e;
352        loop {
353            attr_target = match attr_target {
354                Expr::Assign(e) => &mut e.left,
355                Expr::Binary(e) => &mut e.left,
356                Expr::Cast(e) => &mut e.expr,
357                Expr::Array(_)
358                | Expr::Async(_)
359                | Expr::Await(_)
360                | Expr::Block(_)
361                | Expr::Break(_)
362                | Expr::Call(_)
363                | Expr::Closure(_)
364                | Expr::Const(_)
365                | Expr::Continue(_)
366                | Expr::Field(_)
367                | Expr::ForLoop(_)
368                | Expr::Group(_)
369                | Expr::If(_)
370                | Expr::Index(_)
371                | Expr::Infer(_)
372                | Expr::Let(_)
373                | Expr::Lit(_)
374                | Expr::Loop(_)
375                | Expr::Macro(_)
376                | Expr::Match(_)
377                | Expr::MethodCall(_)
378                | Expr::Paren(_)
379                | Expr::Path(_)
380                | Expr::Range(_)
381                | Expr::RawAddr(_)
382                | Expr::Reference(_)
383                | Expr::Repeat(_)
384                | Expr::Return(_)
385                | Expr::Struct(_)
386                | Expr::Try(_)
387                | Expr::TryBlock(_)
388                | Expr::Tuple(_)
389                | Expr::Unary(_)
390                | Expr::Unsafe(_)
391                | Expr::While(_)
392                | Expr::Yield(_)
393                | Expr::Verbatim(_) => break,
394            };
395        }
396
397        if !attrs.is_empty() {
398            if let Expr::Verbatim(expr_tokens) = attr_target {
399                let mut attr_tokens = verbatim::between(begin, attrs_end);
400                attr_tokens.extend(mem::replace(expr_tokens, TokenStream::new()));
401                *expr_tokens = attr_tokens;
402            } else {
403                let inner_attrs = attr_target.replace_attrs(Vec::new());
404                attrs.extend(inner_attrs);
405                attr_target.replace_attrs(attrs);
406            }
407        }
408
409        let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
410
411        match e {
412            Expr::Macro(ExprMacro { attrs, mac })
413                if semi_token.is_some() || mac.delimiter.is_brace() =>
414            {
415                return Ok(Stmt::Macro(StmtMacro {
416                    attrs,
417                    mac,
418                    semi_token,
419                }));
420            }
421            _ => {}
422        }
423
424        if semi_token.is_some() {
425            Ok(Stmt::Expr(e, semi_token))
426        } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
427            Ok(Stmt::Expr(e, None))
428        } else {
429            Err(input.error("expected semicolon"))
430        }
431    }
432}
433
434#[cfg(feature = "printing")]
435pub(crate) mod printing {
436    use crate::classify;
437    use crate::expr::{self, Expr};
438    use crate::fixup::FixupContext;
439    use crate::stmt::{Block, Local, Stmt, StmtMacro};
440    use crate::token;
441    use proc_macro2::TokenStream;
442    use quote::{ToTokens, TokenStreamExt as _};
443
444    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
445    impl ToTokens for Block {
446        fn to_tokens(&self, tokens: &mut TokenStream) {
447            self.brace_token.surround(tokens, |tokens| {
448                tokens.append_all(&self.stmts);
449            });
450        }
451    }
452
453    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
454    impl ToTokens for Stmt {
455        fn to_tokens(&self, tokens: &mut TokenStream) {
456            match self {
457                Stmt::Local(local) => local.to_tokens(tokens),
458                Stmt::Item(item) => item.to_tokens(tokens),
459                Stmt::Expr(expr, semi) => {
460                    expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
461                    semi.to_tokens(tokens);
462                }
463                Stmt::Macro(mac) => mac.to_tokens(tokens),
464            }
465        }
466    }
467
468    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
469    impl ToTokens for Local {
470        fn to_tokens(&self, tokens: &mut TokenStream) {
471            expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
472            self.let_token.to_tokens(tokens);
473            self.pat.to_tokens(tokens);
474            if let Some(init) = &self.init {
475                init.eq_token.to_tokens(tokens);
476                expr::printing::print_subexpression(
477                    &init.expr,
478                    init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
479                    tokens,
480                    FixupContext::NONE,
481                );
482                if let Some((else_token, diverge)) = &init.diverge {
483                    else_token.to_tokens(tokens);
484                    match &**diverge {
485                        Expr::Block(diverge) => diverge.to_tokens(tokens),
486                        _ => token::Brace::default().surround(tokens, |tokens| {
487                            expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
488                        }),
489                    }
490                }
491            }
492            self.semi_token.to_tokens(tokens);
493        }
494    }
495
496    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
497    impl ToTokens for StmtMacro {
498        fn to_tokens(&self, tokens: &mut TokenStream) {
499            expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
500            self.mac.to_tokens(tokens);
501            self.semi_token.to_tokens(tokens);
502        }
503    }
504}