solar_parse/parser/
stmt.rs

1use super::item::VarFlags;
2use crate::{PResult, Parser, parser::SeqSep};
3use smallvec::SmallVec;
4use solar_ast::{token::*, *};
5use solar_data_structures::BumpExt;
6use solar_interface::{Ident, Span, kw, sym};
7
8impl<'sess, 'ast> Parser<'sess, 'ast> {
9    /// Parses a statement.
10    #[instrument(level = "debug", skip_all)]
11    pub fn parse_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
12        let docs = self.parse_doc_comments();
13        self.parse_spanned(Self::parse_stmt_kind).map(|(span, kind)| Stmt { docs, kind, span })
14    }
15
16    /// Parses a statement into a new allocation.
17    pub fn parse_stmt_boxed(&mut self) -> PResult<'sess, Box<'ast, Stmt<'ast>>> {
18        self.parse_stmt().map(|stmt| self.alloc(stmt))
19    }
20
21    /// Parses a statement kind.
22    fn parse_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
23        let mut semi = true;
24        let kind = if self.eat_keyword(kw::If) {
25            semi = false;
26            self.parse_stmt_if()
27        } else if self.eat_keyword(kw::While) {
28            semi = false;
29            self.parse_stmt_while()
30        } else if self.eat_keyword(kw::Do) {
31            self.parse_stmt_do_while()
32        } else if self.eat_keyword(kw::For) {
33            semi = false;
34            self.parse_stmt_for()
35        } else if self.eat_keyword(kw::Unchecked) {
36            semi = false;
37            self.parse_block().map(StmtKind::UncheckedBlock)
38        } else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
39            semi = false;
40            self.parse_block().map(StmtKind::Block)
41        } else if self.eat_keyword(kw::Continue) {
42            Ok(StmtKind::Continue)
43        } else if self.eat_keyword(kw::Break) {
44            Ok(StmtKind::Break)
45        } else if self.eat_keyword(kw::Return) {
46            let expr = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
47            Ok(StmtKind::Return(expr))
48        } else if self.eat_keyword(kw::Throw) {
49            let msg = "`throw` statements have been removed; use `revert`, `require`, or `assert` instead";
50            Err(self.dcx().err(msg).span(self.prev_token.span))
51        } else if self.eat_keyword(kw::Try) {
52            semi = false;
53            self.parse_stmt_try().map(|stmt| StmtKind::Try(self.alloc(stmt)))
54        } else if self.eat_keyword(kw::Assembly) {
55            semi = false;
56            self.parse_stmt_assembly().map(StmtKind::Assembly)
57        } else if self.eat_keyword(kw::Emit) {
58            self.parse_path_call().map(|(path, params)| StmtKind::Emit(path, params))
59        } else if self.check_keyword(kw::Revert) && self.look_ahead(1).is_ident() {
60            self.bump(); // `revert`
61            self.parse_path_call().map(|(path, params)| StmtKind::Revert(path, params))
62        } else if self.check_keyword(sym::underscore) && self.look_ahead(1).kind == TokenKind::Semi
63        {
64            self.bump(); // `_`
65            Ok(StmtKind::Placeholder)
66        } else {
67            self.parse_simple_stmt_kind()
68        };
69        if semi && kind.is_ok() {
70            self.expect_semi()?;
71        }
72        kind
73    }
74
75    /// Parses a block of statements.
76    pub(super) fn parse_block(&mut self) -> PResult<'sess, Block<'ast>> {
77        let lo = self.token.span;
78        self.parse_delim_seq(Delimiter::Brace, SeqSep::none(), true, Self::parse_stmt)
79            .map(|stmts| Block { span: lo.to(self.prev_token.span), stmts })
80    }
81
82    /// Parses an if statement.
83    fn parse_stmt_if(&mut self) -> PResult<'sess, StmtKind<'ast>> {
84        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
85        let expr = self.parse_expr()?;
86        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
87        let true_stmt = self.parse_stmt()?;
88        let else_stmt =
89            if self.eat_keyword(kw::Else) { Some(self.parse_stmt_boxed()?) } else { None };
90        Ok(StmtKind::If(expr, self.alloc(true_stmt), else_stmt))
91    }
92
93    /// Parses a while statement.
94    fn parse_stmt_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
95        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
96        let expr = self.parse_expr()?;
97        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
98        let stmt = self.parse_stmt()?;
99        Ok(StmtKind::While(expr, self.alloc(stmt)))
100    }
101
102    /// Parses a do-while statement.
103    fn parse_stmt_do_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
104        let stmt = self.parse_stmt()?;
105        let stmt = self.alloc(stmt);
106        self.expect_keyword(kw::While)?;
107        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
108        let expr = self.parse_expr()?;
109        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
110        Ok(StmtKind::DoWhile(stmt, expr))
111    }
112
113    /// Parses a for statement.
114    fn parse_stmt_for(&mut self) -> PResult<'sess, StmtKind<'ast>> {
115        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
116
117        let init = if self.check(TokenKind::Semi) { None } else { Some(self.parse_simple_stmt()?) };
118        self.expect(TokenKind::Semi)?;
119
120        let cond = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
121        self.expect_semi()?;
122
123        let next = if self.check_noexpect(TokenKind::CloseDelim(Delimiter::Parenthesis)) {
124            None
125        } else {
126            Some(self.parse_expr()?)
127        };
128        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
129        let body = self.parse_stmt_boxed()?;
130        Ok(StmtKind::For { init: init.map(|init| self.alloc(init)), cond, next, body })
131    }
132
133    /// Parses a try statement.
134    fn parse_stmt_try(&mut self) -> PResult<'sess, StmtTry<'ast>> {
135        let expr = self.parse_expr()?;
136        let mut clauses = SmallVec::<[_; 4]>::new();
137
138        let mut lo = self.token.span;
139        let returns = if self.eat_keyword(kw::Returns) {
140            self.parse_parameter_list(false, VarFlags::FUNCTION)?
141        } else {
142            Default::default()
143        };
144        let block = self.parse_block()?;
145        let span = lo.to(self.prev_token.span);
146        clauses.push(TryCatchClause { name: None, args: returns, block, span });
147
148        lo = self.token.span;
149        self.expect_keyword(kw::Catch)?;
150        loop {
151            let name = self.parse_ident_opt()?;
152            let args = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
153                self.parse_parameter_list(false, VarFlags::FUNCTION)?
154            } else {
155                Default::default()
156            };
157            let block = self.parse_block()?;
158            let span = lo.to(self.prev_token.span);
159            clauses.push(TryCatchClause { name, args, block, span });
160            lo = self.token.span;
161            if !self.eat_keyword(kw::Catch) {
162                break;
163            }
164        }
165
166        let clauses = self.alloc_smallvec(clauses);
167        Ok(StmtTry { expr, clauses })
168    }
169
170    /// Parses an assembly block.
171    fn parse_stmt_assembly(&mut self) -> PResult<'sess, StmtAssembly<'ast>> {
172        let dialect = self.parse_str_lit_opt();
173        let flags = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
174            self.parse_paren_comma_seq(false, Self::parse_str_lit)?
175        } else {
176            Default::default()
177        };
178        let block = self.parse_yul_block()?;
179        Ok(StmtAssembly { dialect, flags, block })
180    }
181
182    /// Parses a simple statement. These are just variable declarations and expressions.
183    fn parse_simple_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
184        let docs = self.parse_doc_comments();
185        self.parse_spanned(Self::parse_simple_stmt_kind).map(|(span, kind)| Stmt {
186            docs,
187            kind,
188            span,
189        })
190    }
191
192    /// Parses a simple statement kind. These are just variable declarations and expressions.
193    ///
194    /// Also used in the for loop initializer. Does not parse the trailing semicolon.
195    fn parse_simple_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
196        let lo = self.token.span;
197        if self.eat(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
198            let mut empty_components = 0usize;
199            while self.eat(TokenKind::Comma) {
200                empty_components += 1;
201            }
202
203            let (statement_type, iap) = self.try_parse_iap()?;
204            match statement_type {
205                LookAheadInfo::VariableDeclaration => {
206                    let mut variables = smallvec_repeat_none(empty_components);
207                    let ty = iap.into_ty(self);
208                    variables
209                        .push(Some(self.parse_variable_definition_with(VarFlags::FUNCTION, ty)?));
210                    self.parse_optional_items_seq_required(
211                        Delimiter::Parenthesis,
212                        &mut variables,
213                        |this| this.parse_variable_definition(VarFlags::FUNCTION),
214                    )?;
215                    self.expect(TokenKind::Eq)?;
216                    let expr = self.parse_expr()?;
217                    Ok(StmtKind::DeclMulti(self.alloc_smallvec(variables), expr))
218                }
219                LookAheadInfo::Expression => {
220                    let mut components = smallvec_repeat_none(empty_components);
221                    let expr = iap.into_expr(self);
222                    components.push(Some(self.parse_expr_with(expr)?));
223                    self.parse_optional_items_seq_required(
224                        Delimiter::Parenthesis,
225                        &mut components,
226                        Self::parse_expr,
227                    )?;
228                    let partially_parsed = Expr {
229                        span: lo.to(self.prev_token.span),
230                        kind: ExprKind::Tuple(self.alloc_smallvec(components)),
231                    };
232                    self.parse_expr_with(Some(self.alloc(partially_parsed))).map(StmtKind::Expr)
233                }
234                LookAheadInfo::IndexAccessStructure => unreachable!(),
235            }
236        } else {
237            let (statement_type, iap) = self.try_parse_iap()?;
238            match statement_type {
239                LookAheadInfo::VariableDeclaration => {
240                    let ty = iap.into_ty(self);
241                    self.parse_variable_definition_with(VarFlags::VAR, ty)
242                        .map(|var| StmtKind::DeclSingle(self.alloc(var)))
243                }
244                LookAheadInfo::Expression => {
245                    let expr = iap.into_expr(self);
246                    self.parse_expr_with(expr).map(StmtKind::Expr)
247                }
248                LookAheadInfo::IndexAccessStructure => unreachable!(),
249            }
250        }
251    }
252
253    /// Parses a `delim`-delimited, comma-separated list of maybe-optional items.
254    /// E.g. `(a, b) => [Some, Some]`, `(, a,, b,) => [None, Some, None, Some, None]`.
255    pub(super) fn parse_optional_items_seq<T>(
256        &mut self,
257        delim: Delimiter,
258        mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
259    ) -> PResult<'sess, Box<'ast, [Option<T>]>> {
260        self.expect(TokenKind::OpenDelim(delim))?;
261        let mut out = SmallVec::<[_; 8]>::new();
262        while self.eat(TokenKind::Comma) {
263            out.push(None);
264        }
265        if !self.check(TokenKind::CloseDelim(delim)) {
266            out.push(Some(f(self)?));
267        }
268        self.parse_optional_items_seq_required(delim, &mut out, f)
269            .map(|()| self.alloc_smallvec(out))
270    }
271
272    fn parse_optional_items_seq_required<T>(
273        &mut self,
274        delim: Delimiter,
275        out: &mut SmallVec<[Option<T>; 8]>,
276        mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
277    ) -> PResult<'sess, ()> {
278        let close = TokenKind::CloseDelim(delim);
279        while !self.eat(close) {
280            self.expect(TokenKind::Comma)?;
281            if self.check(TokenKind::Comma) || self.check(close) {
282                out.push(None);
283            } else {
284                out.push(Some(f(self)?));
285            }
286        }
287        Ok(())
288    }
289
290    /// Parses a path and a list of call arguments.
291    fn parse_path_call(&mut self) -> PResult<'sess, (AstPath<'ast>, CallArgs<'ast>)> {
292        let path = self.parse_path()?;
293        let params = self.parse_call_args()?;
294        Ok((path, params))
295    }
296
297    /// Never returns `LookAheadInfo::IndexAccessStructure`.
298    fn try_parse_iap(&mut self) -> PResult<'sess, (LookAheadInfo, IndexAccessedPath<'ast>)> {
299        // https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L1961
300        if let ty @ (LookAheadInfo::VariableDeclaration | LookAheadInfo::Expression) =
301            self.peek_statement_type()
302        {
303            return Ok((ty, IndexAccessedPath::default()));
304        }
305
306        let iap = self.parse_iap()?;
307        let ty = if self.token.is_non_reserved_ident(self.in_yul)
308            || self.token.is_location_specifier()
309        {
310            // `a.b memory`, `a[b] c`
311            LookAheadInfo::VariableDeclaration
312        } else {
313            LookAheadInfo::Expression
314        };
315        Ok((ty, iap))
316    }
317
318    fn peek_statement_type(&mut self) -> LookAheadInfo {
319        // https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2528
320        if self.token.is_keyword_any(&[kw::Mapping, kw::Function]) {
321            return LookAheadInfo::VariableDeclaration;
322        }
323
324        if self.check_nr_ident() || self.check_elementary_type() {
325            let next = self.look_ahead(1);
326            if self.token.is_elementary_type() && next.is_ident_where(|id| id.name == kw::Payable) {
327                return LookAheadInfo::VariableDeclaration;
328            }
329            if next.is_non_reserved_ident(self.in_yul)
330                || next.is_location_specifier()
331                // These aren't valid but we include them for a better error message.
332                || next.is_mutability_specifier()
333                || next.is_visibility_specifier()
334            {
335                return LookAheadInfo::VariableDeclaration;
336            }
337            if matches!(next.kind, TokenKind::OpenDelim(Delimiter::Bracket) | TokenKind::Dot) {
338                return LookAheadInfo::IndexAccessStructure;
339            }
340        }
341        LookAheadInfo::Expression
342    }
343
344    fn parse_iap(&mut self) -> PResult<'sess, IndexAccessedPath<'ast>> {
345        // https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2559
346        let mut path = SmallVec::<[_; 4]>::new();
347        if self.check_nr_ident() {
348            path.push(IapKind::Member(self.parse_ident()?));
349            while self.eat(TokenKind::Dot) {
350                let id = self.ident_or_err(true)?;
351                if id.name != kw::Address && id.is_reserved(self.in_yul) {
352                    self.expected_ident_found_err().emit();
353                }
354                self.bump(); // `id`
355                path.push(IapKind::Member(id));
356            }
357        } else if self.check_elementary_type() {
358            let (span, kind) = self.parse_spanned(Self::parse_elementary_type)?;
359            path.push(IapKind::MemberTy(span, kind));
360        } else {
361            return self.unexpected();
362        }
363        let n_idents = path.len();
364
365        while self.check(TokenKind::OpenDelim(Delimiter::Bracket)) {
366            let (span, kind) = self.parse_spanned(Self::parse_expr_index_kind)?;
367            path.push(IapKind::Index(span, kind));
368        }
369
370        Ok(IndexAccessedPath { path, n_idents })
371    }
372}
373
374#[derive(Debug)]
375enum LookAheadInfo {
376    /// `a.b`, `a[b]`
377    IndexAccessStructure,
378    VariableDeclaration,
379    Expression,
380}
381
382#[derive(Debug)]
383enum IapKind<'ast> {
384    /// `[...]`
385    Index(Span, IndexKind<'ast>),
386    /// `<ident>` or `.<ident>`
387    Member(Ident),
388    /// `<ty>`
389    MemberTy(Span, ElementaryType),
390}
391
392#[derive(Debug, Default)]
393struct IndexAccessedPath<'ast> {
394    path: SmallVec<[IapKind<'ast>; 4]>,
395    /// The number of elements in `path` that are `IapKind::Member[Ty]` at the start.
396    n_idents: usize,
397}
398
399impl<'ast> IndexAccessedPath<'ast> {
400    fn into_ty(self, parser: &mut Parser<'_, 'ast>) -> Option<Type<'ast>> {
401        // https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2617
402        let mut path = self.path.into_iter();
403        let first = path.next()?;
404
405        let mut ty = if let IapKind::MemberTy(span, kind) = first {
406            debug_assert_eq!(self.n_idents, 1);
407            Type { span, kind: TypeKind::Elementary(kind) }
408        } else {
409            debug_assert!(self.n_idents >= 1);
410            let first = std::iter::once(&first);
411            let path = first
412                .chain(path.as_slice())
413                .map(|x| match x {
414                    IapKind::Member(id) => *id,
415                    kind => unreachable!("{kind:?}"),
416                })
417                .take(self.n_idents);
418            let path = PathSlice::from_mut_slice(parser.arena.alloc_from_iter(path));
419            Type { span: path.span(), kind: TypeKind::Custom(path) }
420        };
421
422        for index in path.skip(self.n_idents - 1) {
423            let IapKind::Index(span, kind) = index else { panic!("parsed too much") };
424            let size = match kind {
425                IndexKind::Index(expr) => expr,
426                IndexKind::Range(l, r) => {
427                    let msg = "expected array length, got range expression";
428                    parser.dcx().err(msg).span(span).emit();
429                    l.or(r)
430                }
431            };
432            let span = ty.span.to(span);
433            ty =
434                Type { span, kind: TypeKind::Array(parser.alloc(TypeArray { element: ty, size })) };
435        }
436
437        Some(ty)
438    }
439
440    fn into_expr(self, parser: &mut Parser<'_, 'ast>) -> Option<Box<'ast, Expr<'ast>>> {
441        // https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2658
442        let mut path = self.path.into_iter();
443
444        let mut expr = parser.alloc(match path.next()? {
445            IapKind::Member(ident) => Expr::from_ident(ident),
446            IapKind::MemberTy(span, kind) => {
447                Expr { span, kind: ExprKind::Type(Type { span, kind: TypeKind::Elementary(kind) }) }
448            }
449            IapKind::Index(..) => panic!("should not happen"),
450        });
451        for index in path {
452            expr = parser.alloc(match index {
453                IapKind::Member(ident) => {
454                    Expr { span: expr.span.to(ident.span), kind: ExprKind::Member(expr, ident) }
455                }
456                IapKind::MemberTy(..) => panic!("should not happen"),
457                IapKind::Index(span, kind) => {
458                    Expr { span: expr.span.to(span), kind: ExprKind::Index(expr, kind) }
459                }
460            });
461        }
462        Some(expr)
463    }
464}
465
466/// `T: !Clone`
467fn smallvec_repeat_none<T>(n: usize) -> SmallVec<[Option<T>; 8]> {
468    let mut v = SmallVec::with_capacity(n);
469    v.extend(std::iter::repeat_with(|| None).take(n));
470    v
471}
472
473#[cfg(test)]
474mod tests {
475    use super::*;
476    use solar_interface::{Result, Session, source_map::FileName};
477
478    #[test]
479    fn optional_items_seq() {
480        fn check(tests: &[(&str, &[Option<&str>])]) {
481            solar_interface::enter(|| -> Result {
482                let sess = Session::builder().with_test_emitter().build();
483                for (i, &(s, results)) in tests.iter().enumerate() {
484                    let name = i.to_string();
485                    let arena = Arena::new();
486                    let mut parser =
487                        Parser::from_source_code(&sess, &arena, FileName::Custom(name), s)?;
488
489                    let list = parser
490                        .parse_optional_items_seq(Delimiter::Parenthesis, Parser::parse_ident)
491                        .map_err(|e| e.emit())
492                        .unwrap_or_else(|_| panic!("src: {s:?}"));
493                    sess.dcx.has_errors().unwrap();
494                    let formatted: Vec<_> =
495                        list.iter().map(|o| o.as_ref().map(|i| i.as_str())).collect();
496                    assert_eq!(formatted.as_slice(), results, "{s:?}");
497                }
498                Ok(())
499            })
500            .unwrap();
501        }
502
503        check(&[
504            ("()", &[]),
505            ("(a)", &[Some("a")]),
506            // ("(,)", &[None, None]),
507            ("(a,)", &[Some("a"), None]),
508            ("(,b)", &[None, Some("b")]),
509            ("(a,b)", &[Some("a"), Some("b")]),
510            ("(a,b,)", &[Some("a"), Some("b"), None]),
511            // ("(,,)", &[None, None, None]),
512            ("(a,,)", &[Some("a"), None, None]),
513            ("(a,b,)", &[Some("a"), Some("b"), None]),
514            ("(a,b,c)", &[Some("a"), Some("b"), Some("c")]),
515            ("(,b,c)", &[None, Some("b"), Some("c")]),
516            ("(,,c)", &[None, None, Some("c")]),
517            ("(a,,c)", &[Some("a"), None, Some("c")]),
518        ]);
519    }
520}