1use super::*;
2
3ast_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
13ast_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
35ast_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
47ast_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
61ast_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")]
76pub(crate) mod parsing {
77 use super::*;
78 use crate::parse::discouraged::Speculative;
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")))]
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 {
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 {
176 fn parse(input: ParseStream) -> Result<Self> {
177 let allow_nosemi = AllowNoSemi(false);
178 parse_stmt(input, allow_nosemi)
179 }
180 }
181
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]) {
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
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
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
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")]
397mod 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 {
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 {
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 {
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 {
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