syn_solidity/yul/stmt/
switch.rs

1use crate::{Lit, Spanned, YulBlock, YulExpr, kw};
2use proc_macro2::Span;
3use std::fmt;
4use syn::parse::{Parse, ParseStream, Result};
5
6/// A Yul switch statement can consist of only a default-case or one
7/// or more non-default cases optionally followed by a default-case.
8///
9/// Example switch statement in Yul:
10///
11/// ```solidity
12/// switch exponent
13/// case 0 { result := 1 }
14/// case 1 { result := base }
15/// default { revert(0, 0) }
16/// ```
17///
18/// Solidity Reference:
19/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulSwitchStatement>
20#[derive(Clone)]
21pub struct YulSwitch {
22    pub switch_token: kw::switch,
23    pub selector: YulExpr,
24    pub branches: Vec<YulCaseBranch>,
25    pub default_case: Option<YulSwitchDefault>,
26}
27
28impl Parse for YulSwitch {
29    fn parse(input: ParseStream<'_>) -> Result<Self> {
30        let switch_token = input.parse()?;
31        let selector = input.parse()?;
32        let branches = {
33            let mut branches = vec![];
34            while input.peek(kw::case) {
35                branches.push(input.parse()?);
36            }
37            branches
38        };
39        let default_case = { if input.peek(kw::default) { Some(input.parse()?) } else { None } };
40
41        if branches.is_empty() && default_case.is_none() {
42            return Err(input.error("Must have at least one case or a default case."));
43        }
44
45        Ok(Self { switch_token, selector, branches, default_case })
46    }
47}
48
49impl Spanned for YulSwitch {
50    fn span(&self) -> Span {
51        let span = self.switch_token.span();
52        if let Some(default_case) = &self.default_case {
53            return span.join(default_case.span()).unwrap_or(span);
54        }
55        span.join(self.branches.span()).unwrap_or(span)
56    }
57
58    fn set_span(&mut self, span: Span) {
59        self.switch_token.set_span(span);
60        self.selector.set_span(span);
61        self.branches.set_span(span);
62        self.default_case.set_span(span);
63    }
64}
65
66impl fmt::Debug for YulSwitch {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        f.debug_struct("YulSwitch")
69            .field("selector", &self.selector)
70            .field("branches", &self.branches)
71            .field("default_case", &self.default_case)
72            .finish()
73    }
74}
75
76/// Represents a non-default case of a Yul switch statement.
77#[derive(Clone)]
78pub struct YulCaseBranch {
79    pub case_token: kw::case,
80    pub constant: Lit,
81    pub body: YulBlock,
82}
83
84impl Parse for YulCaseBranch {
85    fn parse(input: ParseStream<'_>) -> Result<Self> {
86        Ok(Self { case_token: input.parse()?, constant: input.parse()?, body: input.parse()? })
87    }
88}
89
90impl Spanned for YulCaseBranch {
91    fn span(&self) -> Span {
92        let span = self.case_token.span();
93        span.join(self.body.span()).unwrap_or(span)
94    }
95
96    fn set_span(&mut self, span: Span) {
97        self.case_token.set_span(span);
98        self.constant.set_span(span);
99        self.body.set_span(span);
100    }
101}
102
103impl fmt::Debug for YulCaseBranch {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("YulCaseBranch")
106            .field("constant", &self.constant)
107            .field("body", &self.body)
108            .finish()
109    }
110}
111
112/// Represents the default case of a Yul switch statement.
113#[derive(Clone)]
114pub struct YulSwitchDefault {
115    pub default_token: kw::default,
116    pub body: YulBlock,
117}
118
119impl Parse for YulSwitchDefault {
120    fn parse(input: ParseStream<'_>) -> Result<Self> {
121        Ok(Self { default_token: input.parse()?, body: input.parse()? })
122    }
123}
124
125impl Spanned for YulSwitchDefault {
126    fn span(&self) -> Span {
127        let span = self.default_token.span();
128        span.join(self.body.span()).unwrap_or(span)
129    }
130
131    fn set_span(&mut self, span: Span) {
132        self.default_token.set_span(span);
133        self.body.set_span(span);
134    }
135}
136
137impl fmt::Debug for YulSwitchDefault {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.debug_struct("SwitchDefault").field("body", &self.body).finish()
140    }
141}