solar_ast/ast/
expr.rs

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