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