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