syn_solidity/expr/
mod.rs

1use crate::{
2    kw, utils::ParseNested, Lit, LitDenominated, SolIdent, Spanned, SubDenomination, Type,
3};
4use proc_macro2::{Ident, Span};
5use std::fmt;
6use syn::{
7    ext::IdentExt,
8    parse::{Parse, ParseStream},
9    token::{Brace, Bracket, Paren},
10    Result, Token,
11};
12
13mod array;
14pub use array::{ExprArray, ExprIndex};
15
16mod args;
17pub use args::{
18    ArgList, ArgListImpl, ExprCall, ExprCallOptions, ExprPayable, NamedArg, NamedArgList,
19};
20
21mod binary;
22pub use binary::{BinOp, ExprBinary};
23
24mod member;
25pub use member::ExprMember;
26
27mod ternary;
28pub use ternary::ExprTernary;
29
30mod tuple;
31pub use tuple::ExprTuple;
32
33mod r#type;
34pub use r#type::{ExprNew, ExprTypeCall};
35
36mod unary;
37pub use unary::{ExprDelete, ExprPostfix, ExprUnary, PostUnOp, UnOp};
38
39/// An expression.
40///
41/// Solidity reference:
42/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.expression>
43#[derive(Clone)]
44pub enum Expr {
45    /// An array literal expression: `[a, b, c, d]`.
46    Array(ExprArray),
47
48    /// A binary operation: `a + b`, `a += b`.
49    Binary(ExprBinary),
50
51    /// A function call expression: `foo(42)` or `foo({ bar: 42 })`.
52    Call(ExprCall),
53
54    /// Function call options: `foo.bar{ value: 1, gas: 2 }`.
55    CallOptions(ExprCallOptions),
56
57    /// A unary `delete` expression: `delete vector`.
58    Delete(ExprDelete),
59
60    /// An identifier: `foo`.
61    Ident(SolIdent),
62
63    /// A square bracketed indexing expression: `vector[2]`.
64    Index(ExprIndex),
65
66    /// A literal: `hex"1234"`.
67    Lit(Lit),
68
69    /// A number literal with a sub-denomination: `1 ether`.
70    LitDenominated(LitDenominated),
71
72    /// Access of a named member: `obj.k`.
73    Member(ExprMember),
74
75    /// A `new` expression: `new Contract`.
76    New(ExprNew),
77
78    /// A `payable` expression: `payable(address(0x...))`.
79    Payable(ExprPayable),
80
81    /// A postfix unary expression: `foo++`.
82    Postfix(ExprPostfix),
83
84    /// A ternary (AKA conditional) expression: `foo ? bar : baz`.
85    Ternary(ExprTernary),
86
87    /// A tuple expression: `(a, b, c, d)`.
88    Tuple(ExprTuple),
89
90    /// A type name.
91    ///
92    /// Cannot be `Custom`, as custom identifiers are parsed as `Ident` instead.
93    Type(Type),
94
95    /// A `type()` expression: `type(uint256)`
96    TypeCall(ExprTypeCall),
97
98    /// A unary operation: `!x`, `-x`.
99    Unary(ExprUnary),
100}
101
102impl fmt::Debug for Expr {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str("Expr::")?;
105        match self {
106            Self::Array(expr) => expr.fmt(f),
107            Self::Binary(expr) => expr.fmt(f),
108            Self::Call(expr) => expr.fmt(f),
109            Self::CallOptions(expr) => expr.fmt(f),
110            Self::Delete(expr) => expr.fmt(f),
111            Self::Ident(ident) => ident.fmt(f),
112            Self::Index(expr) => expr.fmt(f),
113            Self::Lit(lit) => lit.fmt(f),
114            Self::LitDenominated(lit) => lit.fmt(f),
115            Self::Member(expr) => expr.fmt(f),
116            Self::New(expr) => expr.fmt(f),
117            Self::Payable(expr) => expr.fmt(f),
118            Self::Postfix(expr) => expr.fmt(f),
119            Self::Ternary(expr) => expr.fmt(f),
120            Self::Tuple(expr) => expr.fmt(f),
121            Self::Type(ty) => ty.fmt(f),
122            Self::TypeCall(expr) => expr.fmt(f),
123            Self::Unary(expr) => expr.fmt(f),
124        }
125    }
126}
127
128impl Parse for Expr {
129    fn parse(input: ParseStream<'_>) -> Result<Self> {
130        // skip any attributes
131        let _ = input.call(syn::Attribute::parse_outer)?;
132
133        debug!("  > Expr: {:?}", input.to_string());
134        let mut expr = Self::parse_simple(input)?;
135        debug!("  < Expr: {expr:?}");
136        loop {
137            let (new, cont) = Self::parse_nested(expr, input)?;
138            if cont {
139                debug!(" << Expr: {new:?}");
140                expr = new;
141            } else {
142                return Ok(new);
143            }
144        }
145    }
146}
147
148impl Spanned for Expr {
149    fn span(&self) -> Span {
150        match self {
151            Self::Array(expr) => expr.span(),
152            Self::Binary(expr) => expr.span(),
153            Self::Call(expr) => expr.span(),
154            Self::CallOptions(expr) => expr.span(),
155            Self::Delete(expr) => expr.span(),
156            Self::Ident(ident) => ident.span(),
157            Self::Index(expr) => expr.span(),
158            Self::Lit(lit) => lit.span(),
159            Self::LitDenominated(lit) => lit.span(),
160            Self::Member(expr) => expr.span(),
161            Self::New(expr) => expr.span(),
162            Self::Payable(expr) => expr.span(),
163            Self::Postfix(expr) => expr.span(),
164            Self::Ternary(expr) => expr.span(),
165            Self::Tuple(expr) => expr.span(),
166            Self::Type(ty) => ty.span(),
167            Self::TypeCall(expr) => expr.span(),
168            Self::Unary(expr) => expr.span(),
169        }
170    }
171
172    fn set_span(&mut self, span: Span) {
173        match self {
174            Self::Array(expr) => expr.set_span(span),
175            Self::Binary(expr) => expr.set_span(span),
176            Self::Call(expr) => expr.set_span(span),
177            Self::CallOptions(expr) => expr.set_span(span),
178            Self::Delete(expr) => expr.set_span(span),
179            Self::Ident(ident) => ident.set_span(span),
180            Self::Index(expr) => expr.set_span(span),
181            Self::Lit(lit) => lit.set_span(span),
182            Self::LitDenominated(lit) => lit.set_span(span),
183            Self::Member(expr) => expr.set_span(span),
184            Self::New(expr) => expr.set_span(span),
185            Self::Payable(expr) => expr.set_span(span),
186            Self::Postfix(expr) => expr.set_span(span),
187            Self::Ternary(expr) => expr.set_span(span),
188            Self::Tuple(expr) => expr.set_span(span),
189            Self::Type(ty) => ty.set_span(span),
190            Self::TypeCall(expr) => expr.set_span(span),
191            Self::Unary(expr) => expr.set_span(span),
192        }
193    }
194}
195
196impl Expr {
197    pub fn peel_parens(&self) -> &Self {
198        let mut expr = self;
199        while let Some(inner) = expr.peel_paren() {
200            expr = inner;
201        }
202        expr
203    }
204
205    fn peel_paren(&self) -> Option<&Self> {
206        if let Self::Tuple(t) = self {
207            if t.elems.len() == 1 {
208                return Some(&t.elems[0]);
209            }
210        }
211        None
212    }
213
214    fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
215        let lookahead = input.lookahead1();
216        if lookahead.peek(Paren) {
217            input.parse().map(Self::Tuple)
218        } else if lookahead.peek(Bracket) {
219            input.parse().map(Self::Array)
220        } else if UnOp::peek(input, &lookahead) {
221            input.parse().map(Self::Unary)
222        } else if Lit::peek(&lookahead) {
223            match (input.parse()?, input.call(SubDenomination::parse_opt)?) {
224                (Lit::Number(number), Some(denom)) => {
225                    Ok(Self::LitDenominated(LitDenominated { number, denom }))
226                }
227                (lit, None) => Ok(Self::Lit(lit)),
228                (_, Some(denom)) => {
229                    Err(syn::Error::new(denom.span(), "unexpected subdenomination for literal"))
230                }
231            }
232        } else if lookahead.peek(kw::payable) {
233            input.parse().map(Self::Payable)
234        } else if lookahead.peek(Token![type]) {
235            input.parse().map(Self::TypeCall)
236        } else if lookahead.peek(kw::new) {
237            input.parse().map(Self::New)
238        } else if lookahead.peek(kw::delete) {
239            input.parse().map(Self::Delete)
240        } else if lookahead.peek(Ident::peek_any) {
241            let ident = input.call(Ident::parse_any)?;
242            let ty = Type::parse_ident(ident.clone()).parse_payable(input);
243            if ty.is_custom() {
244                Ok(Self::Ident(ident.into()))
245            } else {
246                Ok(Self::Type(ty))
247            }
248        } else {
249            Err(lookahead.error())
250        }
251    }
252
253    /// Parse an expression that starts with an expression.
254    ///
255    /// Returns `(ParseResult, continue_parsing)`
256    fn parse_nested(expr: Self, input: ParseStream<'_>) -> Result<(Self, bool)> {
257        macro_rules! parse {
258            (break) => {
259                Ok((expr, false))
260            };
261
262            ($map:expr) => {
263                ParseNested::parse_nested(expr.into(), input).map(|e| ($map(e), true))
264            };
265        }
266
267        let lookahead = input.lookahead1();
268        if lookahead.peek(Bracket) {
269            parse!(Self::Index)
270        } else if lookahead.peek(Brace) {
271            // Special case: `try` stmt block
272            if input.peek2(kw::catch) {
273                parse!(break)
274            } else {
275                parse!(Self::CallOptions)
276            }
277        } else if lookahead.peek(Paren) {
278            parse!(Self::Call)
279        } else if lookahead.peek(Token![.]) {
280            parse!(Self::Member)
281        } else if lookahead.peek(Token![?]) {
282            parse!(Self::Ternary)
283        } else if PostUnOp::peek(input, &lookahead) {
284            parse!(Self::Postfix)
285        } else if BinOp::peek(input, &lookahead) {
286            parse!(Self::Binary)
287        } else {
288            parse!(break)
289        }
290    }
291}