1use crate::attr::Attribute;
2use crate::expr::Expr;
3use crate::item::Item;
4use crate::mac::Macro;
5use crate::pat::Pat;
6use crate::token;
7use alloc::boxed::Box;
8use alloc::vec::Vec;
9
10#[doc = r" A braced block containing Rust statements."]
pub struct Block {
pub brace_token: token::Brace,
#[doc = r" Statements in a block"]
pub stmts: Vec<Stmt>,
}ast_struct! {
11 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
13 pub struct Block {
14 pub brace_token: token::Brace,
15 pub stmts: Vec<Stmt>,
17 }
18}
19
20#[doc = r" A statement, usually ending in a semicolon."]
pub enum Stmt {
#[doc = r" A local (let) binding."]
Local(Local),
#[doc = r" An item definition."]
Item(Item),
#[doc = r" Expression, with or without trailing semicolon."]
Expr(Expr, Option<crate::token::Semi>),
#[doc = r" A macro invocation in statement position."]
#[doc = r""]
#[doc =
r" Syntactically it's ambiguous which other kind of statement this"]
#[doc =
r" macro would expand to. It can be any of local variable (`let`),"]
#[doc = r" item, or expression."]
Macro(StmtMacro),
}ast_enum! {
21 #[cfg_attr(docsrs, doc(cfg(feature = "full")))]
23 pub enum Stmt {
24 Local(Local),
26
27 Item(Item),
29
30 Expr(Expr, Option<Token![;]>),
32
33 Macro(StmtMacro),
39 }
40}
41
42#[doc = r" A local `let` binding: `let x: u64 = s.parse()?;`."]
pub struct Local {
pub attrs: Vec<Attribute>,
pub let_token: crate::token::Let,
pub pat: Pat,
pub init: Option<LocalInit>,
pub semi_token: crate::token::Semi,
}ast_struct! {
43 #[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#[doc =
r" The expression assigned in a local `let` binding, including optional"]
#[doc = r" diverging `else` block."]
#[doc = r""]
#[doc =
r" `LocalInit` represents `= s.parse()?` in `let x: u64 = s.parse()?` and"]
#[doc = r" `= r else { return }` in `let Ok(x) = r else { return }`."]
pub struct LocalInit {
pub eq_token: crate::token::Eq,
pub expr: Box<Expr>,
pub diverge: Option<(crate::token::Else, Box<Expr>)>,
}ast_struct! {
55 #[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#[doc = r" A macro invocation in statement position."]
#[doc = r""]
#[doc =
r" Syntactically it's ambiguous which other kind of statement this macro"]
#[doc =
r" would expand to. It can be any of local variable (`let`), item, or"]
#[doc = r" expression."]
pub struct StmtMacro {
pub attrs: Vec<Attribute>,
pub mac: Macro,
pub semi_token: Option<crate::token::Semi>,
}ast_struct! {
69 #[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")]
83pub(crate) mod parsing {
84 use crate::attr::Attribute;
85 use crate::buffer::Cursor;
86 use crate::classify;
87 use crate::error::Result;
88 use crate::expr::{Expr, ExprBlock, ExprMacro};
89 use crate::ident::Ident;
90 use crate::item;
91 use crate::mac::{self, Macro};
92 use crate::parse::discouraged::Speculative as _;
93 use crate::parse::{Parse, ParseStream};
94 use crate::pat::{Pat, PatType};
95 use crate::path::Path;
96 use crate::stmt::{Block, Local, LocalInit, Stmt, StmtMacro};
97 use crate::token;
98 use crate::ty::Type;
99 use crate::verbatim;
100 use alloc::boxed::Box;
101 use alloc::vec::Vec;
102 use core::mem;
103 use proc_macro2::TokenStream;
104
105 struct AllowNoSemi(bool);
106
107 impl Block {
108 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
158 pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
159 let mut stmts = Vec::new();
160 loop {
161 while let semi @ Some(_) = input.parse()? {
162 stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
163 }
164 if input.is_empty() {
165 break;
166 }
167 let stmt = parse_stmt(input, AllowNoSemi(true))?;
168 let requires_semicolon = match &stmt {
169 Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
170 Stmt::Macro(stmt) => {
171 stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
172 }
173 Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
174 };
175 stmts.push(stmt);
176 if input.is_empty() {
177 break;
178 } else if requires_semicolon {
179 return Err(input.error("unexpected token, expected `;`"));
180 }
181 }
182 Ok(stmts)
183 }
184 }
185
186 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
187 impl Parse for Block {
188 fn parse(input: ParseStream) -> Result<Self> {
189 let content;
190 Ok(Block {
191 brace_token: match crate::__private::parse_braces(&input) {
crate::__private::Ok(braces) => {
content = braces.content;
_ = content;
braces.token
}
crate::__private::Err(error) => { return crate::__private::Err(error); }
}braced!(content in input),
192 stmts: content.call(Block::parse_within)?,
193 })
194 }
195 }
196
197 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
198 impl Parse for Stmt {
199 fn parse(input: ParseStream) -> Result<Self> {
200 let allow_nosemi = AllowNoSemi(false);
201 parse_stmt(input, allow_nosemi)
202 }
203 }
204
205 fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
206 let begin = input.cursor();
207 let attrs = input.call(Attribute::parse_outer)?;
208 let attrs_end = input.cursor();
209
210 let ahead = input.fork();
213 let mut is_item_macro = false;
214 if let Ok(path) = ahead.call(Path::parse_mod_style) {
215 if ahead.peek(crate::token::NotToken![!]) {
216 if ahead.peek2(Ident) || ahead.peek2(crate::token::TryToken![try]) {
217 is_item_macro = true;
218 } else if ahead.peek2(token::Brace)
219 && !(ahead.peek3(crate::token::DotToken![.]) && !ahead.peek3(crate::token::DotDotToken![..])
220 || ahead.peek3(crate::token::QuestionToken![?]))
221 {
222 input.advance_to(&ahead);
223 return stmt_mac(input, attrs, path).map(Stmt::Macro);
224 }
225 }
226 }
227
228 if input.peek(crate::token::LetToken![let]) && !input.peek(token::Group) {
229 stmt_local(input, attrs).map(Stmt::Local)
230 } else if input.peek(crate::token::PubToken![pub])
231 || input.peek(crate::token::CrateToken![crate]) && !input.peek2(crate::token::PathSepToken![::])
232 || input.peek(crate::token::ExternToken![extern])
233 || input.peek(crate::token::UseToken![use])
234 || input.peek(crate::token::StaticToken![static])
235 && (input.peek2(crate::token::MutToken![mut])
236 || input.peek2(Ident)
237 && !(input.peek2(crate::token::AsyncToken![async])
238 && (input.peek3(crate::token::MoveToken![move]) || input.peek3(crate::token::OrToken![|]))))
239 || input.peek(crate::token::ConstToken![const])
240 && !(input.peek2(token::Brace)
241 || input.peek2(crate::token::StaticToken![static])
242 || input.peek2(crate::token::AsyncToken![async])
243 && !(input.peek3(crate::token::UnsafeToken![unsafe])
244 || input.peek3(crate::token::ExternToken![extern])
245 || input.peek3(crate::token::FnToken![fn]))
246 || input.peek2(crate::token::MoveToken![move])
247 || input.peek2(crate::token::OrToken![|]))
248 || input.peek(crate::token::UnsafeToken![unsafe]) && !input.peek2(token::Brace)
249 || input.peek(crate::token::AsyncToken![async])
250 && (input.peek2(crate::token::UnsafeToken![unsafe])
251 || input.peek2(crate::token::ExternToken![extern])
252 || input.peek2(crate::token::FnToken![fn]))
253 || input.peek(crate::token::FnToken![fn])
254 || input.peek(crate::token::ModToken![mod])
255 || input.peek(crate::token::TypeToken![type])
256 || input.peek(crate::token::StructToken![struct])
257 || input.peek(crate::token::EnumToken![enum])
258 || input.peek(crate::token::UnionToken![union]) && input.peek2(Ident)
259 || input.peek(crate::token::AutoToken![auto]) && input.peek2(crate::token::TraitToken![trait])
260 || input.peek(crate::token::TraitToken![trait])
261 || input.peek(crate::token::DefaultToken![default])
262 && (input.peek2(crate::token::UnsafeToken![unsafe]) || input.peek2(crate::token::ImplToken![impl]))
263 || input.peek(crate::token::ImplToken![impl])
264 || input.peek(crate::token::MacroToken![macro])
265 || is_item_macro
266 {
267 let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
268 Ok(Stmt::Item(item))
269 } else {
270 stmt_expr(begin, input, allow_nosemi, attrs, attrs_end)
271 }
272 }
273
274 fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
275 let bang_token: crate::token::NotToken![!] = input.parse()?;
276 let (delimiter, tokens) = mac::parse_delimiter(input)?;
277 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
278
279 Ok(StmtMacro {
280 attrs,
281 mac: Macro {
282 path,
283 bang_token,
284 delimiter,
285 tokens,
286 },
287 semi_token,
288 })
289 }
290
291 fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
292 let let_token: crate::token::LetToken![let] = input.parse()?;
293
294 let mut pat = Pat::parse_single(input)?;
295 if input.peek(crate::token::ColonToken![:]) {
296 let colon_token: crate::token::ColonToken![:] = input.parse()?;
297 let ty: Type = input.parse()?;
298 pat = Pat::Type(PatType {
299 attrs: Vec::new(),
300 pat: Box::new(pat),
301 colon_token,
302 ty: Box::new(ty),
303 });
304 }
305
306 let init = if let Some(eq_token) = input.parse()? {
307 let eq_token: crate::token::EqToken![=] = eq_token;
308 let expr: Expr = input.parse()?;
309
310 let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(crate::token::ElseToken![else]) {
311 let else_token: crate::token::ElseToken![else] = input.parse()?;
312 let diverge = ExprBlock {
313 attrs: Vec::new(),
314 label: None,
315 block: input.parse()?,
316 };
317 Some((else_token, Box::new(Expr::Block(diverge))))
318 } else {
319 None
320 };
321
322 Some(LocalInit {
323 eq_token,
324 expr: Box::new(expr),
325 diverge,
326 })
327 } else {
328 None
329 };
330
331 let semi_token: crate::token::SemiToken![;] = input.parse()?;
332
333 Ok(Local {
334 attrs,
335 let_token,
336 pat,
337 init,
338 semi_token,
339 })
340 }
341
342 fn stmt_expr(
343 begin: Cursor,
344 input: ParseStream,
345 allow_nosemi: AllowNoSemi,
346 mut attrs: Vec<Attribute>,
347 attrs_end: Cursor,
348 ) -> Result<Stmt> {
349 let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
350
351 let mut attr_target = &mut e;
352 loop {
353 attr_target = match attr_target {
354 Expr::Assign(e) => &mut e.left,
355 Expr::Binary(e) => &mut e.left,
356 Expr::Cast(e) => &mut e.expr,
357 Expr::Array(_)
358 | Expr::Async(_)
359 | Expr::Await(_)
360 | Expr::Block(_)
361 | Expr::Break(_)
362 | Expr::Call(_)
363 | Expr::Closure(_)
364 | Expr::Const(_)
365 | Expr::Continue(_)
366 | Expr::Field(_)
367 | Expr::ForLoop(_)
368 | Expr::Group(_)
369 | Expr::If(_)
370 | Expr::Index(_)
371 | Expr::Infer(_)
372 | Expr::Let(_)
373 | Expr::Lit(_)
374 | Expr::Loop(_)
375 | Expr::Macro(_)
376 | Expr::Match(_)
377 | Expr::MethodCall(_)
378 | Expr::Paren(_)
379 | Expr::Path(_)
380 | Expr::Range(_)
381 | Expr::RawAddr(_)
382 | Expr::Reference(_)
383 | Expr::Repeat(_)
384 | Expr::Return(_)
385 | Expr::Struct(_)
386 | Expr::Try(_)
387 | Expr::TryBlock(_)
388 | Expr::Tuple(_)
389 | Expr::Unary(_)
390 | Expr::Unsafe(_)
391 | Expr::While(_)
392 | Expr::Yield(_)
393 | Expr::Verbatim(_) => break,
394 };
395 }
396
397 if !attrs.is_empty() {
398 if let Expr::Verbatim(expr_tokens) = attr_target {
399 let mut attr_tokens = verbatim::between(begin, attrs_end);
400 attr_tokens.extend(mem::replace(expr_tokens, TokenStream::new()));
401 *expr_tokens = attr_tokens;
402 } else {
403 let inner_attrs = attr_target.replace_attrs(Vec::new());
404 attrs.extend(inner_attrs);
405 attr_target.replace_attrs(attrs);
406 }
407 }
408
409 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
410
411 match e {
412 Expr::Macro(ExprMacro { attrs, mac })
413 if semi_token.is_some() || mac.delimiter.is_brace() =>
414 {
415 return Ok(Stmt::Macro(StmtMacro {
416 attrs,
417 mac,
418 semi_token,
419 }));
420 }
421 _ => {}
422 }
423
424 if semi_token.is_some() {
425 Ok(Stmt::Expr(e, semi_token))
426 } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
427 Ok(Stmt::Expr(e, None))
428 } else {
429 Err(input.error("expected semicolon"))
430 }
431 }
432}
433
434#[cfg(feature = "printing")]
435pub(crate) mod printing {
436 use crate::classify;
437 use crate::expr::{self, Expr};
438 use crate::fixup::FixupContext;
439 use crate::stmt::{Block, Local, Stmt, StmtMacro};
440 use crate::token;
441 use proc_macro2::TokenStream;
442 use quote::{ToTokens, TokenStreamExt as _};
443
444 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
445 impl ToTokens for Block {
446 fn to_tokens(&self, tokens: &mut TokenStream) {
447 self.brace_token.surround(tokens, |tokens| {
448 tokens.append_all(&self.stmts);
449 });
450 }
451 }
452
453 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
454 impl ToTokens for Stmt {
455 fn to_tokens(&self, tokens: &mut TokenStream) {
456 match self {
457 Stmt::Local(local) => local.to_tokens(tokens),
458 Stmt::Item(item) => item.to_tokens(tokens),
459 Stmt::Expr(expr, semi) => {
460 expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
461 semi.to_tokens(tokens);
462 }
463 Stmt::Macro(mac) => mac.to_tokens(tokens),
464 }
465 }
466 }
467
468 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
469 impl ToTokens for Local {
470 fn to_tokens(&self, tokens: &mut TokenStream) {
471 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
472 self.let_token.to_tokens(tokens);
473 self.pat.to_tokens(tokens);
474 if let Some(init) = &self.init {
475 init.eq_token.to_tokens(tokens);
476 expr::printing::print_subexpression(
477 &init.expr,
478 init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
479 tokens,
480 FixupContext::NONE,
481 );
482 if let Some((else_token, diverge)) = &init.diverge {
483 else_token.to_tokens(tokens);
484 match &**diverge {
485 Expr::Block(diverge) => diverge.to_tokens(tokens),
486 _ => token::Brace::default().surround(tokens, |tokens| {
487 expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
488 }),
489 }
490 }
491 }
492 self.semi_token.to_tokens(tokens);
493 }
494 }
495
496 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
497 impl ToTokens for StmtMacro {
498 fn to_tokens(&self, tokens: &mut TokenStream) {
499 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
500 self.mac.to_tokens(tokens);
501 self.semi_token.to_tokens(tokens);
502 }
503 }
504}