1use super::item::VarFlags;
2use crate::{PResult, Parser, parser::SeqSep};
3use smallvec::SmallVec;
4use solar_ast::{token::*, *};
5use solar_data_structures::CollectAndApply;
6use solar_interface::{Ident, Span, SpannedOption, Symbol, kw, sym};
7
8impl<'sess, 'ast, 'cb> Parser<'sess, 'ast, 'cb> {
9 #[instrument(level = "trace", skip_all)]
11 pub fn parse_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
12 self.with_recursion_limit("statement", |this| {
13 let docs = this.parse_doc_comments();
14 this.parse_spanned(Self::parse_stmt_kind).map(|(span, kind)| Stmt { docs, kind, span })
15 })
16 }
17
18 pub fn parse_stmt_boxed(&mut self) -> PResult<'sess, Box<'ast, Stmt<'ast>>> {
20 self.parse_stmt().map(|stmt| self.alloc(stmt))
21 }
22
23 fn parse_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
25 let mut semi = true;
26 let kind = if self.eat_keyword(kw::If) {
27 semi = false;
28 self.parse_stmt_if()
29 } else if self.eat_keyword(kw::While) {
30 semi = false;
31 self.parse_stmt_while()
32 } else if self.eat_keyword(kw::Do) {
33 self.parse_stmt_do_while()
34 } else if self.eat_keyword(kw::For) {
35 semi = false;
36 self.parse_stmt_for()
37 } else if self.eat_keyword(kw::Unchecked) {
38 semi = false;
39 self.parse_block().map(StmtKind::UncheckedBlock)
40 } else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
41 semi = false;
42 self.parse_block().map(StmtKind::Block)
43 } else if self.eat_keyword(kw::Continue) {
44 Ok(StmtKind::Continue)
45 } else if self.eat_keyword(kw::Break) {
46 Ok(StmtKind::Break)
47 } else if self.eat_keyword(kw::Return) {
48 let expr = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
49 Ok(StmtKind::Return(expr))
50 } else if self.eat_keyword(kw::Throw) {
51 let msg = "`throw` statements have been removed; use `revert`, `require`, or `assert` instead";
52 Err(self.dcx().err(msg).span(self.prev_token.span))
53 } else if self.eat_keyword(kw::Try) {
54 semi = false;
55 self.parse_stmt_try().map(|stmt| StmtKind::Try(self.alloc(stmt)))
56 } else if self.eat_keyword(kw::Assembly) {
57 semi = false;
58 self.parse_stmt_assembly().map(StmtKind::Assembly)
59 } else if self.eat_keyword(kw::Emit) {
60 self.parse_path_call().map(|(path, params)| StmtKind::Emit(path, params))
61 } else if self.check_keyword(kw::Revert) && self.look_ahead(1).is_ident() {
62 self.bump(); self.parse_path_call().map(|(path, params)| StmtKind::Revert(path, params))
64 } else if self.check_keyword(sym::underscore) && self.look_ahead(1).kind == TokenKind::Semi
65 {
66 self.bump(); Ok(StmtKind::Placeholder)
68 } else {
69 self.parse_simple_stmt_kind()
70 };
71 if semi && kind.is_ok() {
72 self.expect_semi()?;
73 }
74 kind
75 }
76
77 pub(super) fn parse_block(&mut self) -> PResult<'sess, Block<'ast>> {
79 let lo = self.token.span;
80 self.parse_delim_seq(Delimiter::Brace, SeqSep::none(), true, Self::parse_stmt)
81 .map(|stmts| Block { span: lo.to(self.prev_token.span), stmts })
82 }
83
84 fn parse_stmt_if(&mut self) -> PResult<'sess, StmtKind<'ast>> {
86 self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
87 let expr = self.parse_expr()?;
88 self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
89 let true_stmt = self.parse_stmt()?;
90 let else_stmt =
91 if self.eat_keyword(kw::Else) { Some(self.parse_stmt_boxed()?) } else { None };
92 Ok(StmtKind::If(expr, self.alloc(true_stmt), else_stmt))
93 }
94
95 fn parse_stmt_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
97 self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
98 let expr = self.parse_expr()?;
99 self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
100 let stmt = self.parse_stmt()?;
101 Ok(StmtKind::While(expr, self.alloc(stmt)))
102 }
103
104 fn parse_stmt_do_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
106 let stmt = self.parse_stmt()?;
107 let stmt = self.alloc(stmt);
108 self.expect_keyword(kw::While)?;
109 self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
110 let expr = self.parse_expr()?;
111 self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
112 Ok(StmtKind::DoWhile(stmt, expr))
113 }
114
115 fn parse_stmt_for(&mut self) -> PResult<'sess, StmtKind<'ast>> {
117 self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
118
119 let init = if self.check(TokenKind::Semi) { None } else { Some(self.parse_simple_stmt()?) };
120 self.expect(TokenKind::Semi)?;
121
122 let cond = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
123 self.expect_semi()?;
124
125 let next = if self.check_noexpect(TokenKind::CloseDelim(Delimiter::Parenthesis)) {
126 None
127 } else {
128 Some(self.parse_expr()?)
129 };
130 self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
131 let body = self.parse_stmt_boxed()?;
132 Ok(StmtKind::For { init: init.map(|init| self.alloc(init)), cond, next, body })
133 }
134
135 fn parse_stmt_try(&mut self) -> PResult<'sess, StmtTry<'ast>> {
137 let expr = self.parse_expr()?;
138 let mut clauses = SmallVec::<[_; 4]>::new();
139
140 let mut lo = self.token.span;
141 let returns = if self.eat_keyword(kw::Returns) {
142 self.parse_parameter_list(false, VarFlags::FUNCTION)?
143 } else {
144 Default::default()
145 };
146 let block = self.parse_block()?;
147 let span = lo.to(self.prev_token.span);
148 clauses.push(TryCatchClause { name: None, args: returns, block, span });
149
150 lo = self.token.span;
151 self.expect_keyword(kw::Catch)?;
152 loop {
153 let name = self.parse_ident_opt()?;
154 let args = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
155 self.parse_parameter_list(false, VarFlags::FUNCTION)?
156 } else {
157 Default::default()
158 };
159 let block = self.parse_block()?;
160 let span = lo.to(self.prev_token.span);
161 clauses.push(TryCatchClause { name, args, block, span });
162 lo = self.token.span;
163 if !self.eat_keyword(kw::Catch) {
164 break;
165 }
166 }
167
168 let clauses = self.alloc_smallvec(clauses);
169 Ok(StmtTry { expr, clauses })
170 }
171
172 fn parse_stmt_assembly(&mut self) -> PResult<'sess, StmtAssembly<'ast>> {
174 let dialect = self.parse_str_lit_opt();
175 let flags = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
176 self.parse_paren_comma_seq(false, Self::parse_str_lit)?
177 } else {
178 Default::default()
179 };
180 let block = self.parse_yul_block()?;
181 Ok(StmtAssembly { dialect, flags, block })
182 }
183
184 fn parse_simple_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
186 let docs = self.parse_doc_comments();
187 self.parse_spanned(Self::parse_simple_stmt_kind).map(|(span, kind)| Stmt {
188 docs,
189 kind,
190 span,
191 })
192 }
193
194 fn parse_simple_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
198 let lo = self.token.span;
199 if self.eat(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
200 let mut none_elements = SmallVec::<[_; 8]>::new();
201 while self.eat(TokenKind::Comma) {
202 none_elements.push(self.prev_token.span.shrink_to_hi());
203 }
204
205 let (statement_type, iap) = self.try_parse_iap()?;
206 match statement_type {
207 LookAheadInfo::VariableDeclaration => {
208 let mut variables = none_elements
209 .into_iter()
210 .map(SpannedOption::None)
211 .collect::<SmallVec<[_; 8]>>();
212 let ty = iap.into_ty(self);
213 variables.push(SpannedOption::Some(
214 self.parse_variable_definition_with(VarFlags::FUNCTION, ty)?,
215 ));
216 self.parse_optional_items_seq_required(
217 Delimiter::Parenthesis,
218 &mut variables,
219 |this| this.parse_variable_definition(VarFlags::FUNCTION),
220 )?;
221 self.expect(TokenKind::Eq)?;
222 let expr = self.parse_expr()?;
223 Ok(StmtKind::DeclMulti(self.alloc_smallvec(variables), expr))
224 }
225 LookAheadInfo::Expression => {
226 let mut components = none_elements
227 .into_iter()
228 .map(SpannedOption::None)
229 .collect::<SmallVec<[_; 8]>>();
230 let expr = iap.into_expr(self);
231 components.push(SpannedOption::Some(self.parse_expr_with(expr)?));
232 self.parse_optional_items_seq_required(
233 Delimiter::Parenthesis,
234 &mut components,
235 Self::parse_expr,
236 )?;
237 let partially_parsed = Expr {
238 span: lo.to(self.prev_token.span),
239 kind: ExprKind::Tuple(self.alloc_smallvec(components)),
240 };
241 self.parse_expr_with(Some(self.alloc(partially_parsed))).map(StmtKind::Expr)
242 }
243 LookAheadInfo::IndexAccessStructure => unreachable!(),
244 }
245 } else {
246 let (statement_type, iap) = self.try_parse_iap()?;
247 match statement_type {
248 LookAheadInfo::VariableDeclaration => {
249 let ty = iap.into_ty(self);
250 self.parse_variable_definition_with(VarFlags::VAR, ty)
251 .map(|var| StmtKind::DeclSingle(self.alloc(var)))
252 }
253 LookAheadInfo::Expression => {
254 let expr = iap.into_expr(self);
255 self.parse_expr_with(expr).map(StmtKind::Expr)
256 }
257 LookAheadInfo::IndexAccessStructure => unreachable!(),
258 }
259 }
260 }
261
262 pub(super) fn parse_optional_items_seq<T>(
268 &mut self,
269 delim: Delimiter,
270 mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
271 ) -> PResult<'sess, SmallVec<[SpannedOption<T>; 8]>> {
272 self.expect(TokenKind::OpenDelim(delim))?;
273
274 let mut out = SmallVec::<[_; 8]>::new();
275
276 while self.eat(TokenKind::Comma) {
278 out.push(SpannedOption::None(self.prev_token.span.shrink_to_lo()));
279 }
280
281 if !self.check(TokenKind::CloseDelim(delim)) {
284 out.push(SpannedOption::Some(f(self)?));
285 }
286
287 self.parse_optional_items_seq_required(delim, &mut out, f)?;
289 Ok(out)
290 }
291
292 fn parse_optional_items_seq_required<T>(
293 &mut self,
294 delim: Delimiter,
295 out: &mut SmallVec<[SpannedOption<T>; 8]>,
296 mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
297 ) -> PResult<'sess, ()> {
298 let (comma, close) = (TokenKind::Comma, TokenKind::CloseDelim(delim));
299
300 if self.eat(close) {
302 return Ok(());
303 }
304
305 self.expect(comma)?;
307
308 loop {
310 let item = if self.check(comma) || self.check(close) { None } else { Some(f(self)?) };
311 if self.eat(comma) {
312 let element = match item {
313 Some(val) => SpannedOption::Some(val),
314 None => SpannedOption::None(self.prev_token.span.shrink_to_lo()),
315 };
316 out.push(element);
317 } else if self.eat(close) {
318 let element = match item {
319 Some(val) => SpannedOption::Some(val),
320 None => SpannedOption::None(self.prev_token.span.shrink_to_lo()),
321 };
322 out.push(element);
323 return Ok(());
324 } else {
325 return self.unexpected();
326 }
327 }
328 }
329
330 fn parse_path_call(&mut self) -> PResult<'sess, (AstPath<'ast>, CallArgs<'ast>)> {
332 let path = self.parse_path()?;
333 let params = self.parse_call_args()?;
334 Ok((path, params))
335 }
336
337 fn try_parse_iap(&mut self) -> PResult<'sess, (LookAheadInfo, IndexAccessedPath<'ast>)> {
339 if let ty @ (LookAheadInfo::VariableDeclaration | LookAheadInfo::Expression) =
341 self.peek_statement_type()
342 {
343 return Ok((ty, IndexAccessedPath::default()));
344 }
345
346 let iap = self.parse_iap()?;
347 let ty = if self.token.is_non_reserved_ident(self.in_yul)
348 || self.token.is_location_specifier()
349 {
350 LookAheadInfo::VariableDeclaration
352 } else {
353 LookAheadInfo::Expression
354 };
355 Ok((ty, iap))
356 }
357
358 fn peek_statement_type(&mut self) -> LookAheadInfo {
359 if self.token.is_keyword_any(&[kw::Mapping, kw::Function]) {
361 return LookAheadInfo::VariableDeclaration;
362 }
363
364 if self.check_nr_ident() || self.check_elementary_type() {
365 let next = self.look_ahead(1);
366 if self.token.is_elementary_type() && next.is_ident_where(|id| id.name == kw::Payable) {
367 return LookAheadInfo::VariableDeclaration;
368 }
369 if next.is_non_reserved_ident(self.in_yul)
370 || next.is_location_specifier()
371 || next.is_mutability_specifier()
373 || next.is_visibility_specifier()
374 {
375 return LookAheadInfo::VariableDeclaration;
376 }
377 if matches!(next.kind, TokenKind::OpenDelim(Delimiter::Bracket) | TokenKind::Dot) {
378 return LookAheadInfo::IndexAccessStructure;
379 }
380 }
381 LookAheadInfo::Expression
382 }
383
384 fn parse_iap(&mut self) -> PResult<'sess, IndexAccessedPath<'ast>> {
385 let mut path = SmallVec::<[_; 4]>::new();
387 if self.check_nr_ident() {
388 path.push(IapKind::Member(self.parse_ident()?));
389 while self.eat(TokenKind::Dot) {
390 let id = match self.ident_or_err(true) {
391 Ok(id) => id,
392 Err(err) => {
393 err.emit();
394 path.push(IapKind::Member(Ident::new(
395 Symbol::DUMMY,
396 self.prev_token.span.shrink_to_hi(),
397 )));
398 break;
399 }
400 };
401 if id.name != kw::Address && id.is_reserved(self.in_yul) {
402 self.expected_ident_found_err().emit();
403 }
404 self.bump(); path.push(IapKind::Member(id));
406 }
407 } else if self.check_elementary_type() {
408 let (span, kind) = self.parse_spanned(Self::parse_elementary_type)?;
409 path.push(IapKind::MemberTy(span, kind));
410 } else {
411 return self.unexpected();
412 }
413 let n_idents = path
414 .iter()
415 .take_while(|kind| matches!(kind, IapKind::Member(_) | IapKind::MemberTy(..)))
416 .count();
417
418 while self.check(TokenKind::OpenDelim(Delimiter::Bracket)) {
419 let (span, kind) = self.parse_spanned(Self::parse_expr_index_kind)?;
420 path.push(IapKind::Index(span, kind));
421 }
422
423 Ok(IndexAccessedPath { path, n_idents })
424 }
425}
426
427#[derive(Debug)]
428enum LookAheadInfo {
429 IndexAccessStructure,
431 VariableDeclaration,
432 Expression,
433}
434
435#[derive(Debug)]
436enum IapKind<'ast> {
437 Index(Span, IndexKind<'ast>),
439 Member(Ident),
441 MemberTy(Span, ElementaryType),
443}
444
445#[derive(Debug, Default)]
446struct IndexAccessedPath<'ast> {
447 path: SmallVec<[IapKind<'ast>; 4]>,
448 n_idents: usize,
450}
451
452impl<'ast> IndexAccessedPath<'ast> {
453 fn into_ty(self, parser: &mut Parser<'_, 'ast, '_>) -> Option<Type<'ast>> {
454 let mut path = self.path.into_iter();
456 let first = path.next()?;
457
458 let mut ty = if let IapKind::MemberTy(span, kind) = first {
459 debug_assert_eq!(self.n_idents, 1);
460 Type { span, kind: TypeKind::Elementary(kind) }
461 } else {
462 debug_assert!(self.n_idents >= 1);
463 let first = std::iter::once(&first);
464 let path = first
465 .chain(path.as_slice())
466 .map(|x| match x {
467 IapKind::Member(id) => *id,
468 kind => unreachable!("{kind:?}"),
469 })
470 .take(self.n_idents);
471 let path = CollectAndApply::collect_and_apply(path, |path| parser.alloc_path(path));
472 Type { span: path.span(), kind: TypeKind::Custom(path) }
473 };
474
475 for index in path.skip(self.n_idents - 1) {
476 let (span, kind) = match index {
477 IapKind::Index(span, kind) => (span, kind),
478 IapKind::Member(_) | IapKind::MemberTy(..) => panic!("parsed too much"),
479 };
480 let size = match kind {
481 IndexKind::Index(expr) => expr,
482 IndexKind::Range(l, r) => {
483 let msg = "expected array length, got range expression";
484 parser.dcx().emit_err(span, msg);
485 l.or(r)
486 }
487 };
488 let span = ty.span.to(span);
489 ty =
490 Type { span, kind: TypeKind::Array(parser.alloc(TypeArray { element: ty, size })) };
491 }
492
493 Some(ty)
494 }
495
496 fn into_expr(self, parser: &mut Parser<'_, 'ast, '_>) -> Option<Box<'ast, Expr<'ast>>> {
497 let mut path = self.path.into_iter();
499
500 let mut expr = parser.alloc(match path.next()? {
501 IapKind::Member(ident) => Expr::from_ident(ident),
502 IapKind::MemberTy(span, kind) => {
503 Expr { span, kind: ExprKind::Type(Type { span, kind: TypeKind::Elementary(kind) }) }
504 }
505 IapKind::Index(..) => panic!("should not happen"),
506 });
507 for index in path {
508 expr = parser.alloc(match index {
509 IapKind::Member(ident) => {
510 Expr { span: expr.span.to(ident.span), kind: ExprKind::Member(expr, ident) }
511 }
512 IapKind::MemberTy(..) => panic!("should not happen"),
513 IapKind::Index(span, kind) => {
514 Expr { span: expr.span.to(span), kind: ExprKind::Index(expr, kind) }
515 }
516 });
517 }
518 Some(expr)
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525 use solar_interface::{Result, Session, source_map::FileName};
526
527 #[test]
528 fn optional_items_seq() {
529 #[allow(clippy::type_complexity)]
530 fn check(tests: &[(&str, &[Option<&str>])]) {
531 let sess = Session::builder().with_test_emitter().single_threaded().build();
532 sess.enter_sequential(|| -> Result {
533 for &(s, results) in tests.iter() {
534 let name = s.to_string();
535 let arena = Arena::new();
536 let mut parser =
537 Parser::from_source_code(&sess, &arena, FileName::Custom(name), s)?;
538
539 let list = parser
540 .parse_optional_items_seq(Delimiter::Parenthesis, Parser::parse_ident)
541 .map_err(|e| e.emit())
542 .unwrap_or_else(|_| panic!("src: {s:?}"));
543 sess.dcx.has_errors().unwrap();
544
545 let formatted: Vec<_> = list
546 .iter()
547 .map(|item| match item {
548 SpannedOption::Some(ident) => {
549 let data = Some(ident.as_str());
550 let snip = sess.source_map().span_to_snippet(ident.span).unwrap();
551 (data, snip)
552 }
553 SpannedOption::None(span) => {
554 let data = None;
555 let snip = sess.source_map().span_to_snippet(*span).unwrap();
556 (data, snip)
557 }
558 })
559 .collect();
560
561 let expected: Vec<_> = results
563 .iter()
564 .map(|&data| (data, data.unwrap_or("").to_string()))
565 .collect();
566
567 assert_eq!(formatted, expected, "{s:?}");
568 }
569 Ok(())
570 })
571 .unwrap();
572 }
573
574 check(&[
575 ("()", &[]),
576 ("(a)", &[Some("a")]),
577 ("(a,)", &[Some("a"), None]),
580 ("(,b)", &[None, Some("b")]),
581 ("(a,b)", &[Some("a"), Some("b")]),
582 ("(a,b,)", &[Some("a"), Some("b"), None]),
583 ("(a,,)", &[Some("a"), None, None]),
586 ("(a,b,)", &[Some("a"), Some("b"), None]),
587 ("(a,b,c)", &[Some("a"), Some("b"), Some("c")]),
588 ("(,b,c)", &[None, Some("b"), Some("c")]),
589 ("(,,c)", &[None, None, Some("c")]),
590 ("(a,,c)", &[Some("a"), None, Some("c")]),
591 ]);
592 }
593}