1use crate::attr::Attribute;
2use crate::expr::Expr;
3use crate::item::Item;
4use crate::mac::Macro;
5use crate::pat::Pat;
6use crate::token;
7
8ast_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
18ast_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
40ast_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
52ast_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
66ast_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")]
81pub(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")))]
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 {
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 {
191 fn parse(input: ParseStream) -> Result<Self> {
192 let allow_nosemi = AllowNoSemi(false);
193 parse_stmt(input, allow_nosemi)
194 }
195 }
196
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
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
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
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")]
412mod 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 {
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 {
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 {
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 {
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