syn_solidity/yul/stmt/
switch.rs

1use crate::{kw, Lit, Spanned, YulBlock, YulExpr};
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 = {
40            if input.peek(kw::default) {
41                Some(input.parse()?)
42            } else {
43                None
44            }
45        };
46
47        if branches.is_empty() && default_case.is_none() {
48            return Err(input.error("Must have at least one case or a default case."));
49        }
50
51        Ok(Self { switch_token, selector, branches, default_case })
52    }
53}
54
55impl Spanned for YulSwitch {
56    fn span(&self) -> Span {
57        let span = self.switch_token.span();
58        if let Some(default_case) = &self.default_case {
59            return span.join(default_case.span()).unwrap_or(span);
60        }
61        span.join(self.branches.span()).unwrap_or(span)
62    }
63
64    fn set_span(&mut self, span: Span) {
65        self.switch_token.set_span(span);
66        self.selector.set_span(span);
67        self.branches.set_span(span);
68        self.default_case.set_span(span);
69    }
70}
71
72impl fmt::Debug for YulSwitch {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("YulSwitch")
75            .field("selector", &self.selector)
76            .field("branches", &self.branches)
77            .field("default_case", &self.default_case)
78            .finish()
79    }
80}
81
82/// Represents a non-default case of a Yul switch statement.
83#[derive(Clone)]
84pub struct YulCaseBranch {
85    pub case_token: kw::case,
86    pub constant: Lit,
87    pub body: YulBlock,
88}
89
90impl Parse for YulCaseBranch {
91    fn parse(input: ParseStream<'_>) -> Result<Self> {
92        Ok(Self { case_token: input.parse()?, constant: input.parse()?, body: input.parse()? })
93    }
94}
95
96impl Spanned for YulCaseBranch {
97    fn span(&self) -> Span {
98        let span = self.case_token.span();
99        span.join(self.body.span()).unwrap_or(span)
100    }
101
102    fn set_span(&mut self, span: Span) {
103        self.case_token.set_span(span);
104        self.constant.set_span(span);
105        self.body.set_span(span);
106    }
107}
108
109impl fmt::Debug for YulCaseBranch {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        f.debug_struct("YulCaseBranch")
112            .field("constant", &self.constant)
113            .field("body", &self.body)
114            .finish()
115    }
116}
117
118/// Represents the default case of a Yul switch statement.
119#[derive(Clone)]
120pub struct YulSwitchDefault {
121    pub default_token: kw::default,
122    pub body: YulBlock,
123}
124
125impl Parse for YulSwitchDefault {
126    fn parse(input: ParseStream<'_>) -> Result<Self> {
127        Ok(Self { default_token: input.parse()?, body: input.parse()? })
128    }
129}
130
131impl Spanned for YulSwitchDefault {
132    fn span(&self) -> Span {
133        let span = self.default_token.span();
134        span.join(self.body.span()).unwrap_or(span)
135    }
136
137    fn set_span(&mut self, span: Span) {
138        self.default_token.set_span(span);
139        self.body.set_span(span);
140    }
141}
142
143impl fmt::Debug for YulSwitchDefault {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        f.debug_struct("SwitchDefault").field("body", &self.body).finish()
146    }
147}