Skip to content

Commit a05c847

Browse files
authored
feat(ast)!: add span to yul switch cases (#493)
Also renames to `branches` to `cases`, and moves the default case into the list.
1 parent d027a43 commit a05c847

File tree

6 files changed

+72
-26
lines changed

6 files changed

+72
-26
lines changed

crates/ast/src/ast/yul.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,25 @@ pub enum StmtKind<'ast> {
157157
#[derive(Debug)]
158158
pub struct StmtSwitch<'ast> {
159159
pub selector: Expr<'ast>,
160-
pub branches: Box<'ast, [StmtSwitchCase<'ast>]>,
161-
pub default_case: Option<Block<'ast>>,
160+
/// The cases of the switch statement. Includes the default case in the last position, if any.
161+
pub cases: Box<'ast, [StmtSwitchCase<'ast>]>,
162+
}
163+
164+
impl<'ast> StmtSwitch<'ast> {
165+
/// Returns the default case of the switch statement, if any.
166+
pub fn default_case(&self) -> Option<&StmtSwitchCase<'ast>> {
167+
self.cases.last().filter(|case| case.constant.is_none())
168+
}
162169
}
163170

164171
/// Represents a non-default case of a Yul switch statement.
165172
///
166173
/// See [`StmtSwitch`] for more information.
167174
#[derive(Debug)]
168175
pub struct StmtSwitchCase<'ast> {
169-
pub constant: Lit<'ast>,
176+
pub span: Span,
177+
/// The constant of the case, if any. `None` for the default case.
178+
pub constant: Option<Lit<'ast>>,
170179
pub body: Block<'ast>,
171180
}
172181

crates/ast/src/visit.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,20 +565,20 @@ declare_visitors! {
565565
}
566566

567567
fn visit_yul_stmt_switch(&mut self, switch: &'ast #mut yul::StmtSwitch<'ast>) -> ControlFlow<Self::BreakValue> {
568-
let yul::StmtSwitch { selector, branches, default_case } = switch;
568+
let yul::StmtSwitch { selector, cases } = switch;
569569
self.visit_yul_expr #_mut(selector)?;
570-
for case in branches.iter #_mut() {
570+
for case in cases.iter #_mut() {
571571
self.visit_yul_stmt_case #_mut(case)?;
572572
}
573-
if let Some(case) = default_case {
574-
self.visit_yul_block #_mut(case)?;
575-
}
576573
ControlFlow::Continue(())
577574
}
578575

579576
fn visit_yul_stmt_case(&mut self, case: &'ast #mut yul::StmtSwitchCase<'ast>) -> ControlFlow<Self::BreakValue> {
580-
let yul::StmtSwitchCase { constant, body } = case;
581-
self.visit_lit #_mut(constant)?;
577+
let yul::StmtSwitchCase { span, constant, body } = case;
578+
self.visit_span #_mut(span)?;
579+
if let Some(constant) = constant {
580+
self.visit_lit #_mut(constant)?;
581+
}
582582
self.visit_yul_block #_mut(body)?;
583583
ControlFlow::Continue(())
584584
}

crates/parse/src/parser/yul.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::SeqSep;
22
use crate::{PResult, Parser};
33
use smallvec::SmallVec;
44
use solar_ast::{
5-
AstPath, Box, DocComments, Lit, LitKind, PathSlice, StrKind, StrLit, token::*, yul::*,
5+
AstPath, Box, DocComments, Lit, LitKind, PathSlice, StrKind, StrLit, Symbol, token::*, yul::*,
66
};
77
use solar_interface::{Ident, error_code, kw, sym};
88

@@ -214,20 +214,16 @@ impl<'sess, 'ast> Parser<'sess, 'ast> {
214214
fn parse_yul_stmt_switch(&mut self) -> PResult<'sess, StmtSwitch<'ast>> {
215215
let lo = self.prev_token.span;
216216
let selector = self.parse_yul_expr()?;
217-
let mut branches = Vec::new();
218-
while self.eat_keyword(kw::Case) {
219-
let constant = self.parse_yul_lit()?;
220-
self.expect_no_subdenomination();
221-
let body = self.parse_yul_block_unchecked()?;
222-
branches.push(StmtSwitchCase { constant, body });
217+
let mut cases = Vec::new();
218+
while self.check_keyword(kw::Case) {
219+
cases.push(self.parse_yul_stmt_switch_case(kw::Case)?);
223220
}
224-
let branches = self.alloc_vec(branches);
225-
let default_case = if self.eat_keyword(kw::Default) {
226-
Some(self.parse_yul_block_unchecked()?)
221+
let default_case = if self.check_keyword(kw::Default) {
222+
Some(self.parse_yul_stmt_switch_case(kw::Default)?)
227223
} else {
228224
None
229225
};
230-
if branches.is_empty() {
226+
if cases.is_empty() {
231227
let span = lo.to(self.prev_token.span);
232228
if default_case.is_none() {
233229
self.dcx().err("`switch` statement has no cases").span(span).emit();
@@ -239,7 +235,28 @@ impl<'sess, 'ast> Parser<'sess, 'ast> {
239235
.emit();
240236
}
241237
}
242-
Ok(StmtSwitch { selector, branches, default_case })
238+
if let Some(default_case) = default_case {
239+
cases.push(default_case);
240+
}
241+
let cases = self.alloc_vec(cases);
242+
Ok(StmtSwitch { selector, cases })
243+
}
244+
245+
fn parse_yul_stmt_switch_case(&mut self, kw: Symbol) -> PResult<'sess, StmtSwitchCase<'ast>> {
246+
self.parse_spanned(|this| {
247+
debug_assert!(this.token.is_keyword(kw));
248+
this.bump();
249+
let constant = if kw == kw::Case {
250+
let lit = this.parse_yul_lit()?;
251+
this.expect_no_subdenomination();
252+
Some(lit)
253+
} else {
254+
None
255+
};
256+
let body = this.parse_yul_block_unchecked()?;
257+
Ok((constant, body))
258+
})
259+
.map(|(span, (constant, body))| StmtSwitchCase { span, constant, body })
243260
}
244261

245262
/// Parses a Yul for statement.

crates/sema/src/stats.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,9 @@ impl<'ast> Visit<'ast> for StatCollector {
474474
) -> ControlFlow<Self::BreakValue> {
475475
self.record("YulStmtSwitch", None, switch);
476476
// Don't visit selector field since it isn't boxed
477-
for case in switch.branches.iter() {
477+
for case in switch.cases.iter() {
478478
self.visit_yul_stmt_case(case)?;
479479
}
480-
if let Some(case) = &switch.default_case {
481-
self.visit_yul_block(case)?;
482-
}
483480
ControlFlow::Continue(())
484481
}
485482

tests/ui/parser/bad_switch.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
function f() {
2+
assembly {
3+
switch 42 //~ ERROR: `switch` statement has no cases
4+
5+
switch 42 //~ WARN: `switch` statement has only a default case
6+
default {}
7+
}
8+
}

tests/ui/parser/bad_switch.stderr

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: `switch` statement has no cases
2+
--> ROOT/tests/ui/parser/bad_switch.sol:LL:CC
3+
|
4+
LL | switch 42
5+
| ^^^^^^^^^
6+
7+
warning[9592]: `switch` statement has only a default case
8+
--> ROOT/tests/ui/parser/bad_switch.sol:LL:CC
9+
|
10+
LL | / switch 42
11+
LL | | default {}
12+
| |__________________^
13+
14+
error: aborting due to 1 previous error; 1 warning emitted
15+

0 commit comments

Comments
 (0)