solar_ast/ast/
expr.rs

1use super::{Box, Lit, SubDenomination, Type};
2use either::Either;
3use solar_interface::{Ident, Span};
4use std::fmt;
5
6/// A list of named arguments: `{a: "1", b: 2}`.
7///
8/// Present in [`CallArgsKind::Named`] and [`ExprKind::CallOptions`].
9pub type NamedArgList<'ast> = Box<'ast, [NamedArg<'ast>]>;
10
11/// An expression.
12///
13/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.expression>
14#[derive(Debug)]
15pub struct Expr<'ast> {
16    pub span: Span,
17    pub kind: ExprKind<'ast>,
18}
19
20impl AsRef<Self> for Expr<'_> {
21    fn as_ref(&self) -> &Self {
22        self
23    }
24}
25
26impl<'ast> Expr<'ast> {
27    /// Creates a new expression from an identifier.
28    pub fn from_ident(ident: Ident) -> Self {
29        Self { span: ident.span, kind: ExprKind::Ident(ident) }
30    }
31
32    /// Creates a new expression from a type.
33    pub fn from_ty(ty: Type<'ast>) -> Self {
34        Self { span: ty.span, kind: ExprKind::Type(ty) }
35    }
36}
37
38/// A kind of expression.
39#[derive(Debug)]
40pub enum ExprKind<'ast> {
41    /// An array literal expression: `[a, b, c, d]`.
42    Array(Box<'ast, [Box<'ast, Expr<'ast>>]>),
43
44    /// An assignment: `a = b`, `a += b`.
45    Assign(Box<'ast, Expr<'ast>>, Option<BinOp>, Box<'ast, Expr<'ast>>),
46
47    /// A binary operation: `a + b`, `a >> b`.
48    Binary(Box<'ast, Expr<'ast>>, BinOp, Box<'ast, Expr<'ast>>),
49
50    /// A function call expression: `foo(42)` or `foo({ bar: 42 })`.
51    Call(Box<'ast, Expr<'ast>>, CallArgs<'ast>),
52
53    /// Function call options: `foo.bar{ value: 1, gas: 2 }`.
54    CallOptions(Box<'ast, Expr<'ast>>, NamedArgList<'ast>),
55
56    /// A unary `delete` expression: `delete vector`.
57    Delete(Box<'ast, Expr<'ast>>),
58
59    /// An identifier: `foo`.
60    Ident(Ident),
61
62    /// A square bracketed indexing expression: `vector[index]`, `slice[l:r]`.
63    Index(Box<'ast, Expr<'ast>>, IndexKind<'ast>),
64
65    /// A literal: `hex"1234"`, `5.6 ether`.
66    ///
67    /// Note that the `SubDenomination` is only present for numeric literals, and it's already
68    /// applied to `Lit`'s value. It is only present for error reporting/formatting purposes.
69    Lit(Box<'ast, Lit<'ast>>, Option<SubDenomination>),
70
71    /// Access of a named member: `obj.k`.
72    Member(Box<'ast, Expr<'ast>>, Ident),
73
74    /// A `new` expression: `new Contract`.
75    New(Type<'ast>),
76
77    /// A `payable` expression: `payable(address(0x...))`.
78    Payable(CallArgs<'ast>),
79
80    /// A ternary (AKA conditional) expression: `foo ? bar : baz`.
81    Ternary(Box<'ast, Expr<'ast>>, Box<'ast, Expr<'ast>>, Box<'ast, Expr<'ast>>),
82
83    /// A tuple expression: `(a,,, b, c, d)`.
84    Tuple(Box<'ast, [Option<Box<'ast, Expr<'ast>>>]>),
85
86    /// A `type()` expression: `type(uint256)`.
87    TypeCall(Type<'ast>),
88
89    /// An elementary type name: `uint256`.
90    Type(Type<'ast>),
91
92    /// A unary operation: `!x`, `-x`, `x++`.
93    Unary(UnOp, Box<'ast, Expr<'ast>>),
94}
95
96/// A binary operation: `a + b`, `a += b`.
97#[derive(Clone, Copy, Debug)]
98pub struct BinOp {
99    pub span: Span,
100    pub kind: BinOpKind,
101}
102
103/// A kind of binary operation.
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
105pub enum BinOpKind {
106    /// `<`
107    Lt,
108    /// `<=`
109    Le,
110    /// `>`
111    Gt,
112    /// `>=`
113    Ge,
114    /// `==`
115    Eq,
116    /// `!=`
117    Ne,
118    /// `||`
119    Or,
120    /// `&&`
121    And,
122
123    /// `>>`
124    Shr,
125    /// `<<`
126    Shl,
127    /// `>>>`
128    Sar,
129    /// `&`
130    BitAnd,
131    /// `|`
132    BitOr,
133    /// `^`
134    BitXor,
135
136    /// `+`
137    Add,
138    /// `-`
139    Sub,
140    /// `**`
141    Pow,
142    /// `*`
143    Mul,
144    /// `/`
145    Div,
146    /// `%`
147    Rem,
148}
149
150impl fmt::Display for BinOp {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        f.write_str(self.kind.to_str())
153    }
154}
155
156impl BinOpKind {
157    /// Returns the string representation of the operator.
158    pub const fn to_str(self) -> &'static str {
159        match self {
160            Self::Lt => "<",
161            Self::Le => "<=",
162            Self::Gt => ">",
163            Self::Ge => ">=",
164            Self::Eq => "==",
165            Self::Ne => "!=",
166            Self::Or => "||",
167            Self::And => "&&",
168            Self::Sar => ">>>",
169            Self::Shr => ">>",
170            Self::Shl => "<<",
171            Self::BitAnd => "&",
172            Self::BitOr => "|",
173            Self::BitXor => "^",
174            Self::Add => "+",
175            Self::Sub => "-",
176            Self::Pow => "**",
177            Self::Mul => "*",
178            Self::Div => "/",
179            Self::Rem => "%",
180        }
181    }
182
183    /// Returns `true` if the operator is able to be used in an assignment.
184    pub const fn assignable(self) -> bool {
185        // https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.expression
186        match self {
187            Self::BitOr
188            | Self::BitXor
189            | Self::BitAnd
190            | Self::Shl
191            | Self::Shr
192            | Self::Sar
193            | Self::Add
194            | Self::Sub
195            | Self::Mul
196            | Self::Div
197            | Self::Rem => true,
198
199            Self::Lt
200            | Self::Le
201            | Self::Gt
202            | Self::Ge
203            | Self::Eq
204            | Self::Ne
205            | Self::Or
206            | Self::And
207            | Self::Pow => false,
208        }
209    }
210}
211
212/// A unary operation: `!x`, `-x`, `x++`.
213#[derive(Clone, Copy, Debug)]
214pub struct UnOp {
215    pub span: Span,
216    pub kind: UnOpKind,
217}
218
219/// A kind of unary operation.
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
221pub enum UnOpKind {
222    /// `++x`
223    PreInc,
224    /// `--x`
225    PreDec,
226    /// `!`
227    Not,
228    /// `-`
229    Neg,
230    /// `~`
231    BitNot,
232
233    /// `x++`
234    PostInc,
235    /// `x--`
236    PostDec,
237}
238
239impl fmt::Display for UnOp {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        f.write_str(self.kind.to_str())
242    }
243}
244
245impl UnOpKind {
246    /// Returns the string representation of the operator.
247    pub const fn to_str(self) -> &'static str {
248        match self {
249            Self::PreInc => "++",
250            Self::PreDec => "--",
251            Self::Not => "!",
252            Self::Neg => "-",
253            Self::BitNot => "~",
254            Self::PostInc => "++",
255            Self::PostDec => "--",
256        }
257    }
258
259    /// Returns `true` if the operator is a prefix operator.
260    pub const fn is_prefix(self) -> bool {
261        match self {
262            Self::PreInc | Self::PreDec | Self::Not | Self::Neg | Self::BitNot => true,
263            Self::PostInc | Self::PostDec => false,
264        }
265    }
266
267    /// Returns `true` if the operator is a postfix operator.
268    pub const fn is_postfix(self) -> bool {
269        !self.is_prefix()
270    }
271}
272
273/// A list of function call arguments.
274#[derive(Debug)]
275pub struct CallArgs<'ast> {
276    /// The span of the arguments. This points to the parenthesized list of arguments.
277    ///
278    /// If the list is empty, this points to the empty `()`/`({})` or to where the `(` would be.
279    pub span: Span,
280    pub kind: CallArgsKind<'ast>,
281}
282
283impl<'ast> CallArgs<'ast> {
284    /// Creates a new empty list of arguments.
285    ///
286    /// `span` should be an empty span.
287    pub fn empty(span: Span) -> Self {
288        Self { span, kind: CallArgsKind::empty() }
289    }
290
291    /// Returns `true` if the argument list is not present in the source code.
292    ///
293    /// For example, a modifier `m` can be invoked in a function declaration as `m` or `m()`. In the
294    /// first case, this returns `true`, and the span will point to after `m`. In the second case,
295    /// this returns `false`.
296    pub fn is_dummy(&self) -> bool {
297        self.span.lo() == self.span.hi()
298    }
299
300    /// Returns the length of the arguments.
301    pub fn len(&self) -> usize {
302        self.kind.len()
303    }
304
305    /// Returns `true` if the list of arguments is empty.
306    pub fn is_empty(&self) -> bool {
307        self.kind.is_empty()
308    }
309
310    /// Returns an iterator over the expressions.
311    pub fn exprs(
312        &self,
313    ) -> impl ExactSizeIterator<Item = &Expr<'ast>> + DoubleEndedIterator + Clone {
314        self.kind.exprs()
315    }
316
317    /// Returns an iterator over the expressions.
318    pub fn exprs_mut(
319        &mut self,
320    ) -> impl ExactSizeIterator<Item = &mut Box<'ast, Expr<'ast>>> + DoubleEndedIterator {
321        self.kind.exprs_mut()
322    }
323}
324
325/// A list of function call argument expressions.
326#[derive(Debug)]
327pub enum CallArgsKind<'ast> {
328    /// A list of unnamed arguments: `(1, 2, 3)`.
329    Unnamed(Box<'ast, [Box<'ast, Expr<'ast>>]>),
330
331    /// A list of named arguments: `({x: 1, y: 2, z: 3})`.
332    Named(NamedArgList<'ast>),
333}
334
335impl Default for CallArgsKind<'_> {
336    fn default() -> Self {
337        Self::empty()
338    }
339}
340
341impl<'ast> CallArgsKind<'ast> {
342    /// Creates a new empty list of unnamed arguments.
343    pub fn empty() -> Self {
344        Self::Unnamed(Box::default())
345    }
346
347    /// Returns the length of the arguments.
348    pub fn len(&self) -> usize {
349        match self {
350            Self::Unnamed(exprs) => exprs.len(),
351            Self::Named(args) => args.len(),
352        }
353    }
354
355    /// Returns `true` if the list of arguments is empty.
356    pub fn is_empty(&self) -> bool {
357        self.len() == 0
358    }
359
360    /// Returns an iterator over the expressions.
361    pub fn exprs(
362        &self,
363    ) -> impl ExactSizeIterator<Item = &Expr<'ast>> + DoubleEndedIterator + Clone {
364        match self {
365            Self::Unnamed(exprs) => Either::Left(exprs.iter().map(|expr| &**expr)),
366            Self::Named(args) => Either::Right(args.iter().map(|arg| &*arg.value)),
367        }
368    }
369
370    /// Returns an iterator over the expressions.
371    pub fn exprs_mut(
372        &mut self,
373    ) -> impl ExactSizeIterator<Item = &mut Box<'ast, Expr<'ast>>> + DoubleEndedIterator {
374        match self {
375            Self::Unnamed(exprs) => Either::Left(exprs.iter_mut()),
376            Self::Named(args) => Either::Right(args.iter_mut().map(|arg| &mut arg.value)),
377        }
378    }
379
380    /// Returns the span of the argument expressions. Does not include the parentheses.
381    pub fn span(&self) -> Option<Span> {
382        if self.is_empty() {
383            return None;
384        }
385        Some(Span::join_first_last(self.exprs().map(|e| e.span)))
386    }
387}
388
389/// A named argument: `name: value`.
390#[derive(Debug)]
391pub struct NamedArg<'ast> {
392    pub name: Ident,
393    pub value: Box<'ast, Expr<'ast>>,
394}
395
396/// A kind of square bracketed indexing expression: `vector[index]`, `slice[l:r]`.
397#[derive(Debug)]
398pub enum IndexKind<'ast> {
399    /// A single index: `vector[index]`.
400    Index(Option<Box<'ast, Expr<'ast>>>),
401
402    /// A slice: `slice[l:r]`.
403    Range(Option<Box<'ast, Expr<'ast>>>, Option<Box<'ast, Expr<'ast>>>),
404}