Skip to main content

syn/
stmt.rs

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