syn/
stmt.rs

1use super::*;
2
3ast_struct! {
4    /// A braced block containing Rust statements.
5    ///
6    /// *This type is available if Syn is built with the `"full"` feature.*
7    pub struct Block {
8        pub brace_token: token::Brace,
9        /// Statements in a block
10        pub stmts: Vec<Stmt>,
11    }
12}
13
14ast_enum! {
15    /// A statement, usually ending in a semicolon.
16    ///
17    /// *This type is available if Syn is built with the `"full"` feature.*
18    pub enum Stmt {
19        /// A local (let) binding.
20        Local(Local),
21
22        /// An item definition.
23        Item(Item),
24
25        /// Expr without trailing semicolon.
26        Expr(Expr),
27
28        /// Expression with trailing semicolon.
29        Semi(Expr, Token![;]),
30    }
31}
32
33ast_struct! {
34    /// A local `let` binding: `let x: u64 = s.parse()?`.
35    ///
36    /// *This type is available if Syn is built with the `"full"` feature.*
37    pub struct Local {
38        pub attrs: Vec<Attribute>,
39        pub let_token: Token![let],
40        pub pat: Pat,
41        pub init: Option<(Token![=], Box<Expr>)>,
42        pub semi_token: Token![;],
43    }
44}
45
46#[cfg(feature = "parsing")]
47pub mod parsing {
48    use super::*;
49
50    use crate::parse::{Parse, ParseStream, Result};
51    use crate::punctuated::Punctuated;
52
53    impl Block {
54        /// Parse the body of a block as zero or more statements, possibly
55        /// including one trailing expression.
56        ///
57        /// *This function is available if Syn is built with the `"parsing"`
58        /// feature.*
59        ///
60        /// # Example
61        ///
62        /// ```
63        /// use syn::{braced, token, Attribute, Block, Ident, Result, Stmt, Token};
64        /// use syn::parse::{Parse, ParseStream};
65        ///
66        /// // Parse a function with no generics or parameter list.
67        /// //
68        /// //     fn playground {
69        /// //         let mut x = 1;
70        /// //         x += 1;
71        /// //         println!("{}", x);
72        /// //     }
73        /// struct MiniFunction {
74        ///     attrs: Vec<Attribute>,
75        ///     fn_token: Token![fn],
76        ///     name: Ident,
77        ///     brace_token: token::Brace,
78        ///     stmts: Vec<Stmt>,
79        /// }
80        ///
81        /// impl Parse for MiniFunction {
82        ///     fn parse(input: ParseStream) -> Result<Self> {
83        ///         let outer_attrs = input.call(Attribute::parse_outer)?;
84        ///         let fn_token: Token![fn] = input.parse()?;
85        ///         let name: Ident = input.parse()?;
86        ///
87        ///         let content;
88        ///         let brace_token = braced!(content in input);
89        ///         let inner_attrs = content.call(Attribute::parse_inner)?;
90        ///         let stmts = content.call(Block::parse_within)?;
91        ///
92        ///         Ok(MiniFunction {
93        ///             attrs: {
94        ///                 let mut attrs = outer_attrs;
95        ///                 attrs.extend(inner_attrs);
96        ///                 attrs
97        ///             },
98        ///             fn_token,
99        ///             name,
100        ///             brace_token,
101        ///             stmts,
102        ///         })
103        ///     }
104        /// }
105        /// ```
106        pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
107            let mut stmts = Vec::new();
108            loop {
109                while input.peek(Token![;]) {
110                    input.parse::<Token![;]>()?;
111                }
112                if input.is_empty() {
113                    break;
114                }
115                let s = parse_stmt(input, true)?;
116                let requires_semicolon = if let Stmt::Expr(s) = &s {
117                    expr::requires_terminator(s)
118                } else {
119                    false
120                };
121                stmts.push(s);
122                if input.is_empty() {
123                    break;
124                } else if requires_semicolon {
125                    return Err(input.error("unexpected token"));
126                }
127            }
128            Ok(stmts)
129        }
130    }
131
132    impl Parse for Block {
133        fn parse(input: ParseStream) -> Result<Self> {
134            let content;
135            Ok(Block {
136                brace_token: braced!(content in input),
137                stmts: content.call(Block::parse_within)?,
138            })
139        }
140    }
141
142    impl Parse for Stmt {
143        fn parse(input: ParseStream) -> Result<Self> {
144            parse_stmt(input, false)
145        }
146    }
147
148    fn parse_stmt(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
149        // TODO: optimize using advance_to
150        let ahead = input.fork();
151        ahead.call(Attribute::parse_outer)?;
152
153        if {
154            let ahead = ahead.fork();
155            // Only parse braces here; paren and bracket will get parsed as
156            // expression statements
157            ahead.call(Path::parse_mod_style).is_ok()
158                && ahead.parse::<Token![!]>().is_ok()
159                && (ahead.peek(token::Brace) || ahead.peek(Ident))
160        } {
161            stmt_mac(input)
162        } else if ahead.peek(Token![let]) {
163            stmt_local(input).map(Stmt::Local)
164        } else if ahead.peek(Token![pub])
165            || ahead.peek(Token![crate]) && !ahead.peek2(Token![::])
166            || ahead.peek(Token![extern]) && !ahead.peek2(Token![::])
167            || ahead.peek(Token![use])
168            || ahead.peek(Token![static]) && (ahead.peek2(Token![mut]) || ahead.peek2(Ident))
169            || ahead.peek(Token![const])
170            || ahead.peek(Token![unsafe]) && !ahead.peek2(token::Brace)
171            || ahead.peek(Token![async])
172                && (ahead.peek2(Token![unsafe])
173                    || ahead.peek2(Token![extern])
174                    || ahead.peek2(Token![fn]))
175            || ahead.peek(Token![fn])
176            || ahead.peek(Token![mod])
177            || ahead.peek(Token![type])
178            || ahead.peek(item::parsing::existential) && ahead.peek2(Token![type])
179            || ahead.peek(Token![struct])
180            || ahead.peek(Token![enum])
181            || ahead.peek(Token![union]) && ahead.peek2(Ident)
182            || ahead.peek(Token![auto]) && ahead.peek2(Token![trait])
183            || ahead.peek(Token![trait])
184            || ahead.peek(Token![default])
185                && (ahead.peek2(Token![unsafe]) || ahead.peek2(Token![impl]))
186            || ahead.peek(Token![impl])
187            || ahead.peek(Token![macro])
188        {
189            input.parse().map(Stmt::Item)
190        } else {
191            stmt_expr(input, allow_nosemi)
192        }
193    }
194
195    fn stmt_mac(input: ParseStream) -> Result<Stmt> {
196        let attrs = input.call(Attribute::parse_outer)?;
197        let path = input.call(Path::parse_mod_style)?;
198        let bang_token: Token![!] = input.parse()?;
199        let ident: Option<Ident> = input.parse()?;
200        let (delimiter, tokens) = mac::parse_delimiter(input)?;
201        let semi_token: Option<Token![;]> = input.parse()?;
202
203        Ok(Stmt::Item(Item::Macro(ItemMacro {
204            attrs,
205            ident,
206            mac: Macro {
207                path,
208                bang_token,
209                delimiter,
210                tokens,
211            },
212            semi_token,
213        })))
214    }
215
216    fn stmt_local(input: ParseStream) -> Result<Local> {
217        Ok(Local {
218            attrs: input.call(Attribute::parse_outer)?,
219            let_token: input.parse()?,
220            pat: {
221                let leading_vert: Option<Token![|]> = input.parse()?;
222                let mut pat: Pat = input.parse()?;
223                if leading_vert.is_some()
224                    || input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=])
225                {
226                    let mut cases = Punctuated::new();
227                    cases.push_value(pat);
228                    while input.peek(Token![|])
229                        && !input.peek(Token![||])
230                        && !input.peek(Token![|=])
231                    {
232                        let punct = input.parse()?;
233                        cases.push_punct(punct);
234                        let pat: Pat = input.parse()?;
235                        cases.push_value(pat);
236                    }
237                    pat = Pat::Or(PatOr {
238                        attrs: Vec::new(),
239                        leading_vert,
240                        cases,
241                    });
242                }
243                if input.peek(Token![:]) {
244                    let colon_token: Token![:] = input.parse()?;
245                    let ty: Type = input.parse()?;
246                    pat = Pat::Type(PatType {
247                        attrs: Vec::new(),
248                        pat: Box::new(pat),
249                        colon_token,
250                        ty: Box::new(ty),
251                    });
252                }
253                pat
254            },
255            init: {
256                if input.peek(Token![=]) {
257                    let eq_token: Token![=] = input.parse()?;
258                    let init: Expr = input.parse()?;
259                    Some((eq_token, Box::new(init)))
260                } else {
261                    None
262                }
263            },
264            semi_token: input.parse()?,
265        })
266    }
267
268    fn stmt_expr(input: ParseStream, allow_nosemi: bool) -> Result<Stmt> {
269        let mut attrs = input.call(Attribute::parse_outer)?;
270        let mut e = expr::parsing::expr_early(input)?;
271
272        attrs.extend(e.replace_attrs(Vec::new()));
273        e.replace_attrs(attrs);
274
275        if input.peek(Token![;]) {
276            return Ok(Stmt::Semi(e, input.parse()?));
277        }
278
279        if allow_nosemi || !expr::requires_terminator(&e) {
280            Ok(Stmt::Expr(e))
281        } else {
282            Err(input.error("expected semicolon"))
283        }
284    }
285}
286
287#[cfg(feature = "printing")]
288mod printing {
289    use super::*;
290
291    use proc_macro2::TokenStream;
292    use quote::{ToTokens, TokenStreamExt};
293
294    impl ToTokens for Block {
295        fn to_tokens(&self, tokens: &mut TokenStream) {
296            self.brace_token.surround(tokens, |tokens| {
297                tokens.append_all(&self.stmts);
298            });
299        }
300    }
301
302    impl ToTokens for Stmt {
303        fn to_tokens(&self, tokens: &mut TokenStream) {
304            match self {
305                Stmt::Local(local) => local.to_tokens(tokens),
306                Stmt::Item(item) => item.to_tokens(tokens),
307                Stmt::Expr(expr) => expr.to_tokens(tokens),
308                Stmt::Semi(expr, semi) => {
309                    expr.to_tokens(tokens);
310                    semi.to_tokens(tokens);
311                }
312            }
313        }
314    }
315
316    impl ToTokens for Local {
317        fn to_tokens(&self, tokens: &mut TokenStream) {
318            expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
319            self.let_token.to_tokens(tokens);
320            self.pat.to_tokens(tokens);
321            if let Some((eq_token, init)) = &self.init {
322                eq_token.to_tokens(tokens);
323                init.to_tokens(tokens);
324            }
325            self.semi_token.to_tokens(tokens);
326        }
327    }
328}