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::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 alloc::boxed::Box;
99 use alloc::vec::Vec;
100 use proc_macro2::TokenStream;
101
102 struct AllowNoSemi(bool);
103
104 impl Block {
105 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
155 pub fn parse_within(input: ParseStream) -> Result<Vec<Stmt>> {
156 let mut stmts = Vec::new();
157 loop {
158 while let semi @ Some(_) = input.parse()? {
159 stmts.push(Stmt::Expr(Expr::Verbatim(TokenStream::new()), semi));
160 }
161 if input.is_empty() {
162 break;
163 }
164 let stmt = parse_stmt(input, AllowNoSemi(true))?;
165 let requires_semicolon = match &stmt {
166 Stmt::Expr(stmt, None) => classify::requires_semi_to_be_stmt(stmt),
167 Stmt::Macro(stmt) => {
168 stmt.semi_token.is_none() && !stmt.mac.delimiter.is_brace()
169 }
170 Stmt::Local(_) | Stmt::Item(_) | Stmt::Expr(_, Some(_)) => false,
171 };
172 stmts.push(stmt);
173 if input.is_empty() {
174 break;
175 } else if requires_semicolon {
176 return Err(input.error("unexpected token, expected `;`"));
177 }
178 }
179 Ok(stmts)
180 }
181 }
182
183 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
184 impl Parse for Block {
185 fn parse(input: ParseStream) -> Result<Self> {
186 let content;
187 Ok(Block {
188 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),
189 stmts: content.call(Block::parse_within)?,
190 })
191 }
192 }
193
194 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
195 impl Parse for Stmt {
196 fn parse(input: ParseStream) -> Result<Self> {
197 let allow_nosemi = AllowNoSemi(false);
198 parse_stmt(input, allow_nosemi)
199 }
200 }
201
202 fn parse_stmt(input: ParseStream, allow_nosemi: AllowNoSemi) -> Result<Stmt> {
203 let begin = input.fork();
204 let attrs = input.call(Attribute::parse_outer)?;
205
206 let ahead = input.fork();
209 let mut is_item_macro = false;
210 if let Ok(path) = ahead.call(Path::parse_mod_style) {
211 if ahead.peek(crate::token::NotToken![!]) {
212 if ahead.peek2(Ident) || ahead.peek2(crate::token::TryToken![try]) {
213 is_item_macro = true;
214 } else if ahead.peek2(token::Brace)
215 && !(ahead.peek3(crate::token::DotToken![.]) && !ahead.peek3(crate::token::DotDotToken![..])
216 || ahead.peek3(crate::token::QuestionToken![?]))
217 {
218 input.advance_to(&ahead);
219 return stmt_mac(input, attrs, path).map(Stmt::Macro);
220 }
221 }
222 }
223
224 if input.peek(crate::token::LetToken![let]) && !input.peek(token::Group) {
225 stmt_local(input, attrs).map(Stmt::Local)
226 } else if input.peek(crate::token::PubToken![pub])
227 || input.peek(crate::token::CrateToken![crate]) && !input.peek2(crate::token::PathSepToken![::])
228 || input.peek(crate::token::ExternToken![extern])
229 || input.peek(crate::token::UseToken![use])
230 || input.peek(crate::token::StaticToken![static])
231 && (input.peek2(crate::token::MutToken![mut])
232 || input.peek2(Ident)
233 && !(input.peek2(crate::token::AsyncToken![async])
234 && (input.peek3(crate::token::MoveToken![move]) || input.peek3(crate::token::OrToken![|]))))
235 || input.peek(crate::token::ConstToken![const])
236 && !(input.peek2(token::Brace)
237 || input.peek2(crate::token::StaticToken![static])
238 || input.peek2(crate::token::AsyncToken![async])
239 && !(input.peek3(crate::token::UnsafeToken![unsafe])
240 || input.peek3(crate::token::ExternToken![extern])
241 || input.peek3(crate::token::FnToken![fn]))
242 || input.peek2(crate::token::MoveToken![move])
243 || input.peek2(crate::token::OrToken![|]))
244 || input.peek(crate::token::UnsafeToken![unsafe]) && !input.peek2(token::Brace)
245 || input.peek(crate::token::AsyncToken![async])
246 && (input.peek2(crate::token::UnsafeToken![unsafe])
247 || input.peek2(crate::token::ExternToken![extern])
248 || input.peek2(crate::token::FnToken![fn]))
249 || input.peek(crate::token::FnToken![fn])
250 || input.peek(crate::token::ModToken![mod])
251 || input.peek(crate::token::TypeToken![type])
252 || input.peek(crate::token::StructToken![struct])
253 || input.peek(crate::token::EnumToken![enum])
254 || input.peek(crate::token::UnionToken![union]) && input.peek2(Ident)
255 || input.peek(crate::token::AutoToken![auto]) && input.peek2(crate::token::TraitToken![trait])
256 || input.peek(crate::token::TraitToken![trait])
257 || input.peek(crate::token::DefaultToken![default])
258 && (input.peek2(crate::token::UnsafeToken![unsafe]) || input.peek2(crate::token::ImplToken![impl]))
259 || input.peek(crate::token::ImplToken![impl])
260 || input.peek(crate::token::MacroToken![macro])
261 || is_item_macro
262 {
263 let item = item::parsing::parse_rest_of_item(begin, attrs, input)?;
264 Ok(Stmt::Item(item))
265 } else {
266 stmt_expr(input, allow_nosemi, attrs)
267 }
268 }
269
270 fn stmt_mac(input: ParseStream, attrs: Vec<Attribute>, path: Path) -> Result<StmtMacro> {
271 let bang_token: crate::token::NotToken![!] = input.parse()?;
272 let (delimiter, tokens) = mac::parse_delimiter(input)?;
273 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
274
275 Ok(StmtMacro {
276 attrs,
277 mac: Macro {
278 path,
279 bang_token,
280 delimiter,
281 tokens,
282 },
283 semi_token,
284 })
285 }
286
287 fn stmt_local(input: ParseStream, attrs: Vec<Attribute>) -> Result<Local> {
288 let let_token: crate::token::LetToken![let] = input.parse()?;
289
290 let mut pat = Pat::parse_single(input)?;
291 if input.peek(crate::token::ColonToken![:]) {
292 let colon_token: crate::token::ColonToken![:] = input.parse()?;
293 let ty: Type = input.parse()?;
294 pat = Pat::Type(PatType {
295 attrs: Vec::new(),
296 pat: Box::new(pat),
297 colon_token,
298 ty: Box::new(ty),
299 });
300 }
301
302 let init = if let Some(eq_token) = input.parse()? {
303 let eq_token: crate::token::EqToken![=] = eq_token;
304 let expr: Expr = input.parse()?;
305
306 let diverge = if !classify::expr_trailing_brace(&expr) && input.peek(crate::token::ElseToken![else]) {
307 let else_token: crate::token::ElseToken![else] = input.parse()?;
308 let diverge = ExprBlock {
309 attrs: Vec::new(),
310 label: None,
311 block: input.parse()?,
312 };
313 Some((else_token, Box::new(Expr::Block(diverge))))
314 } else {
315 None
316 };
317
318 Some(LocalInit {
319 eq_token,
320 expr: Box::new(expr),
321 diverge,
322 })
323 } else {
324 None
325 };
326
327 let semi_token: crate::token::SemiToken![;] = input.parse()?;
328
329 Ok(Local {
330 attrs,
331 let_token,
332 pat,
333 init,
334 semi_token,
335 })
336 }
337
338 fn stmt_expr(
339 input: ParseStream,
340 allow_nosemi: AllowNoSemi,
341 mut attrs: Vec<Attribute>,
342 ) -> Result<Stmt> {
343 let mut e = Expr::parse_with_earlier_boundary_rule(input)?;
344
345 let mut attr_target = &mut e;
346 loop {
347 attr_target = match attr_target {
348 Expr::Assign(e) => &mut e.left,
349 Expr::Binary(e) => &mut e.left,
350 Expr::Cast(e) => &mut e.expr,
351 Expr::Array(_)
352 | Expr::Async(_)
353 | Expr::Await(_)
354 | Expr::Block(_)
355 | Expr::Break(_)
356 | Expr::Call(_)
357 | Expr::Closure(_)
358 | Expr::Const(_)
359 | Expr::Continue(_)
360 | Expr::Field(_)
361 | Expr::ForLoop(_)
362 | Expr::Group(_)
363 | Expr::If(_)
364 | Expr::Index(_)
365 | Expr::Infer(_)
366 | Expr::Let(_)
367 | Expr::Lit(_)
368 | Expr::Loop(_)
369 | Expr::Macro(_)
370 | Expr::Match(_)
371 | Expr::MethodCall(_)
372 | Expr::Paren(_)
373 | Expr::Path(_)
374 | Expr::Range(_)
375 | Expr::RawAddr(_)
376 | Expr::Reference(_)
377 | Expr::Repeat(_)
378 | Expr::Return(_)
379 | Expr::Struct(_)
380 | Expr::Try(_)
381 | Expr::TryBlock(_)
382 | Expr::Tuple(_)
383 | Expr::Unary(_)
384 | Expr::Unsafe(_)
385 | Expr::While(_)
386 | Expr::Yield(_)
387 | Expr::Verbatim(_) => break,
388 };
389 }
390 attrs.extend(attr_target.replace_attrs(Vec::new()));
391 attr_target.replace_attrs(attrs);
392
393 let semi_token: Option<crate::token::SemiToken![;]> = input.parse()?;
394
395 match e {
396 Expr::Macro(ExprMacro { attrs, mac })
397 if semi_token.is_some() || mac.delimiter.is_brace() =>
398 {
399 return Ok(Stmt::Macro(StmtMacro {
400 attrs,
401 mac,
402 semi_token,
403 }));
404 }
405 _ => {}
406 }
407
408 if semi_token.is_some() {
409 Ok(Stmt::Expr(e, semi_token))
410 } else if allow_nosemi.0 || !classify::requires_semi_to_be_stmt(&e) {
411 Ok(Stmt::Expr(e, None))
412 } else {
413 Err(input.error("expected semicolon"))
414 }
415 }
416}
417
418#[cfg(feature = "printing")]
419pub(crate) mod printing {
420 use crate::classify;
421 use crate::expr::{self, Expr};
422 use crate::fixup::FixupContext;
423 use crate::stmt::{Block, Local, Stmt, StmtMacro};
424 use crate::token;
425 use proc_macro2::TokenStream;
426 use quote::{ToTokens, TokenStreamExt as _};
427
428 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
429 impl ToTokens for Block {
430 fn to_tokens(&self, tokens: &mut TokenStream) {
431 self.brace_token.surround(tokens, |tokens| {
432 tokens.append_all(&self.stmts);
433 });
434 }
435 }
436
437 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
438 impl ToTokens for Stmt {
439 fn to_tokens(&self, tokens: &mut TokenStream) {
440 match self {
441 Stmt::Local(local) => local.to_tokens(tokens),
442 Stmt::Item(item) => item.to_tokens(tokens),
443 Stmt::Expr(expr, semi) => {
444 expr::printing::print_expr(expr, tokens, FixupContext::new_stmt());
445 semi.to_tokens(tokens);
446 }
447 Stmt::Macro(mac) => mac.to_tokens(tokens),
448 }
449 }
450 }
451
452 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
453 impl ToTokens for Local {
454 fn to_tokens(&self, tokens: &mut TokenStream) {
455 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
456 self.let_token.to_tokens(tokens);
457 self.pat.to_tokens(tokens);
458 if let Some(init) = &self.init {
459 init.eq_token.to_tokens(tokens);
460 expr::printing::print_subexpression(
461 &init.expr,
462 init.diverge.is_some() && classify::expr_trailing_brace(&init.expr),
463 tokens,
464 FixupContext::NONE,
465 );
466 if let Some((else_token, diverge)) = &init.diverge {
467 else_token.to_tokens(tokens);
468 match &**diverge {
469 Expr::Block(diverge) => diverge.to_tokens(tokens),
470 _ => token::Brace::default().surround(tokens, |tokens| {
471 expr::printing::print_expr(diverge, tokens, FixupContext::new_stmt());
472 }),
473 }
474 }
475 }
476 self.semi_token.to_tokens(tokens);
477 }
478 }
479
480 #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
481 impl ToTokens for StmtMacro {
482 fn to_tokens(&self, tokens: &mut TokenStream) {
483 expr::printing::outer_attrs_to_tokens(&self.attrs, tokens);
484 self.mac.to_tokens(tokens);
485 self.semi_token.to_tokens(tokens);
486 }
487 }
488}