Skip to main content

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, diagnostics::ErrorGuaranteed};
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    /// An erroneous expression emitted during parser recovery.
122    Err(ErrorGuaranteed),
123}
124
125/// A binary operation: `a + b`, `a += b`.
126#[derive(Clone, Copy, Debug)]
127pub struct BinOp {
128    pub span: Span,
129    pub kind: BinOpKind,
130}
131
132/// A kind of binary operation.
133#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
134pub enum BinOpKind {
135    /// `<`
136    Lt,
137    /// `<=`
138    Le,
139    /// `>`
140    Gt,
141    /// `>=`
142    Ge,
143    /// `==`
144    Eq,
145    /// `!=`
146    Ne,
147    /// `||`
148    Or,
149    /// `&&`
150    And,
151
152    /// `>>`
153    Shr,
154    /// `<<`
155    Shl,
156    /// `>>>`
157    Sar,
158    /// `&`
159    BitAnd,
160    /// `|`
161    BitOr,
162    /// `^`
163    BitXor,
164
165    /// `+`
166    Add,
167    /// `-`
168    Sub,
169    /// `**`
170    Pow,
171    /// `*`
172    Mul,
173    /// `/`
174    Div,
175    /// `%`
176    Rem,
177}
178
179impl fmt::Display for BinOp {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        f.write_str(self.kind.to_str())
182    }
183}
184
185impl BinOpKind {
186    /// Returns the string representation of the operator.
187    pub const fn to_str(self) -> &'static str {
188        match self {
189            Self::Lt => "<",
190            Self::Le => "<=",
191            Self::Gt => ">",
192            Self::Ge => ">=",
193            Self::Eq => "==",
194            Self::Ne => "!=",
195            Self::Or => "||",
196            Self::And => "&&",
197            Self::Sar => ">>>",
198            Self::Shr => ">>",
199            Self::Shl => "<<",
200            Self::BitAnd => "&",
201            Self::BitOr => "|",
202            Self::BitXor => "^",
203            Self::Add => "+",
204            Self::Sub => "-",
205            Self::Pow => "**",
206            Self::Mul => "*",
207            Self::Div => "/",
208            Self::Rem => "%",
209        }
210    }
211
212    /// Returns `true` if the operator is able to be used in an assignment.
213    pub const fn assignable(self) -> bool {
214        // https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.expression
215        use BinOpKind::*;
216        matches!(self, BitOr | BitXor | BitAnd | Shl | Shr | Sar | Add | Sub | Mul | Div | Rem)
217    }
218
219    /// Returns `true` if the operator is a comparison operator.
220    pub const fn is_cmp(self) -> bool {
221        use BinOpKind::*;
222        matches!(self, Lt | Le | Gt | Ge | Eq | Ne)
223    }
224
225    /// Returns `true` if the operator is a shift operator.
226    pub const fn is_shift(self) -> bool {
227        use BinOpKind::*;
228        matches!(self, Shl | Shr | Sar)
229    }
230}
231
232/// A unary operation: `!x`, `-x`, `x++`.
233#[derive(Clone, Copy, Debug)]
234pub struct UnOp {
235    pub span: Span,
236    pub kind: UnOpKind,
237}
238
239/// A kind of unary operation.
240#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
241pub enum UnOpKind {
242    /// `++x`
243    PreInc,
244    /// `--x`
245    PreDec,
246    /// `!`
247    Not,
248    /// `-`
249    Neg,
250    /// `~`
251    BitNot,
252
253    /// `x++`
254    PostInc,
255    /// `x--`
256    PostDec,
257}
258
259impl fmt::Display for UnOp {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        f.write_str(self.kind.to_str())
262    }
263}
264
265impl UnOpKind {
266    /// Returns the string representation of the operator.
267    pub const fn to_str(self) -> &'static str {
268        match self {
269            Self::PreInc | Self::PostInc => "++",
270            Self::PreDec | Self::PostDec => "--",
271            Self::Not => "!",
272            Self::Neg => "-",
273            Self::BitNot => "~",
274        }
275    }
276
277    /// Returns `true` if the operator is a prefix operator.
278    pub const fn is_prefix(self) -> bool {
279        match self {
280            Self::PreInc | Self::PreDec | Self::Not | Self::Neg | Self::BitNot => true,
281            Self::PostInc | Self::PostDec => false,
282        }
283    }
284
285    /// Returns `true` if the operator is a postfix operator.
286    pub const fn is_postfix(self) -> bool {
287        !self.is_prefix()
288    }
289
290    /// Returns `true` if the operator has side effects.
291    pub const fn has_side_effects(self) -> bool {
292        match self {
293            Self::PreInc | Self::PreDec | Self::PostInc | Self::PostDec => true,
294            Self::Not | Self::Neg | Self::BitNot => false,
295        }
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}