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