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