Skip to main content

rustidy_ast/expr/with_block/
block.rs

1//! Block expression
2
3// Imports
4use {
5	crate::{
6		attr::BracedWithInnerAttributes,
7		expr::ExpressionWithoutBlock,
8		stmt::{
9			ExpressionStatement,
10			ExpressionStatementWithBlock,
11			ExpressionStatementWithoutBlock,
12			Statement,
13		},
14		token,
15	},
16	rustidy_ast_util::NotFollows,
17	rustidy_format::{Format, Formattable, WhitespaceFormat},
18	rustidy_parse::{Parse, ParseError, Parser, ParserError, ParserTag},
19	rustidy_print::Print,
20	rustidy_util::{ArenaIdx, Whitespace, decl_arena},
21};
22
23/// `BlockExpression`
24#[derive(PartialEq, Eq, Clone, Debug)]
25#[derive(serde::Serialize, serde::Deserialize)]
26#[derive(Parse, Formattable, Format, Print)]
27#[parse(name = "a block expression")]
28#[parse(skip_if_tag = ParserTag::SkipBlockExpression)]
29pub struct BlockExpression(pub ArenaIdx<BracedWithInnerAttributes<Statements>>);
30
31/// `Statements`
32#[derive(PartialEq, Eq, Clone, Debug)]
33#[derive(serde::Serialize, serde::Deserialize)]
34#[derive(Formattable, Format, Print)]
35pub struct Statements {
36	#[format(args = rustidy_format::vec::args_prefix_ws(Whitespace::INDENT))]
37	pub stmts:         Vec<Statement>,
38	#[format(prefix_ws(expr = Whitespace::INDENT, if_ = !self.stmts.is_empty()))]
39	pub trailing_expr: Option<ExpressionWithoutBlock>,
40}
41
42decl_arena! { BracedWithInnerAttributes<Statements> }
43
44impl Parse for Statements {
45	type Error = StatementsError;
46
47	#[coverage(on)]
48	fn parse_from(parser: &mut Parser) -> Result<Self, Self::Error> {
49		let mut stmts = vec![];
50		let trailing_expr = loop {
51			// Note: Blocks usually take priority over expressions here, as `{} * a`
52			//       parses as an empty block, followed by the expression `*a`, but
53			//       this is not the case for field/method access and the question mark
54			//       operator.
55			if let Ok((expr, ..)) = parser
56				.try_parse::<(ExpressionStatementWithBlock, NotFollows<token::Dot>, NotFollows<token::Question>,)>()? {
57				stmts.push(
58					Statement::Expression(ExpressionStatement::WithBlock(expr))
59				);
60				continue;
61			}
62
63			match parser
64				.peek::<(ExpressionWithoutBlock, Option<token::Semi>)>()? {
65				Ok(((expr, semi), peek_expr_state)) => match semi {
66					Some(semi) => {
67						parser.set_peeked(peek_expr_state);
68						stmts.push(Statement::Expression(
69							ExpressionStatement::WithoutBlock(ExpressionStatementWithoutBlock { expr, semi },)
70						));
71					},
72					None => match parser.with_tag(
73						ParserTag::SkipExpressionWithoutBlock,
74						Parser::peek::<Statement>
75					)? {
76						// Note: On macros, we want to ensure we parse a statement macro instead of expression macro,
77						//       since braced statement macros don't need a semi-colon, while expression ones do.
78						//       Since both have the same length, we prefer statements to expressions if they have
79						//       the same length here.
80						Ok((stmt, peek_stmt_state)) if peek_stmt_state
81							.ahead_of_or_equal(&peek_expr_state) => {
82							parser.set_peeked(peek_stmt_state);
83							stmts.push(stmt);
84						},
85						_ => {
86							parser.set_peeked(peek_expr_state);
87							break Some(expr);
88						},
89					},
90				},
91				Err(_) => match parser.with_tag(
92					ParserTag::SkipExpressionWithoutBlock,
93					Parser::try_parse::<Statement>
94				)? {
95					Ok(stmt) => stmts.push(stmt),
96					Err(_) => break None,
97				},
98			}
99		};
100
101		Ok(Self { stmts, trailing_expr })
102	}
103}
104
105#[derive(derive_more::Debug, derive_more::From, ParseError)]
106pub enum StatementsError {
107	#[parse_error(transparent)]
108	ExpressionStatementWithBlock(
109		ParserError<(ExpressionStatementWithBlock, NotFollows<token::Dot>, NotFollows<token::Question>,)>,
110	),
111
112	#[parse_error(transparent)]
113	ExpressionWithoutBlock(ParserError<(ExpressionWithoutBlock, Option<token::Semi>)>),
114
115	#[parse_error(transparent)]
116	Statement(ParserError<Statement>),
117}