• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::*;
2 
3 ast_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 
13 ast_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 
35 ast_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 
47 ast_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 
61 ast_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")]
76 pub(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")))]
parse_within(input: ParseStream) -> Result<Vec<Stmt>>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 {
parse(input: ParseStream) -> Result<Self>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 {
parse(input: ParseStream) -> Result<Self>176         fn parse(input: ParseStream) -> Result<Self> {
177             let allow_nosemi = AllowNoSemi(false);
178             parse_stmt(input, allow_nosemi)
179         }
180     }
181 
parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt>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 
stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro>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 
stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local>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 
stmt_expr( input: ParseStream, allow_nosemi: AllowNoSemi, mut attrs: Vec<Attribute>, ) -> Result<Stmt>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")]
397 mod 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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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 {
to_tokens(&self, tokens: &mut TokenStream)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