syn/
stmt.rs

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