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