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