solar_ast/ast/
item.rs

1use super::{
2    AstPath, BinOpKind, Block, Box, CallArgs, DocComments, Expr, SemverReq, StrLit, Type, UnOpKind,
3};
4use crate::{BoxSlice, token::Token};
5use either::Either;
6use solar_interface::{Ident, Span, Spanned, Symbol};
7use std::{
8    fmt,
9    ops::{Deref, DerefMut},
10};
11use strum::EnumIs;
12
13/// A list of variable declarations and its span, which includes the brackets.
14///
15/// Implements `Deref` and `DerefMut` for transparent access to the parameter list.
16#[derive(Debug, Default)]
17pub struct ParameterList<'ast> {
18    pub span: Span,
19    pub vars: BoxSlice<'ast, VariableDefinition<'ast>>,
20}
21
22impl<'ast> Deref for ParameterList<'ast> {
23    type Target = BoxSlice<'ast, VariableDefinition<'ast>>;
24
25    fn deref(&self) -> &Self::Target {
26        &self.vars
27    }
28}
29
30impl<'ast> DerefMut for ParameterList<'ast> {
31    fn deref_mut(&mut self) -> &mut Self::Target {
32        &mut self.vars
33    }
34}
35
36/// A top-level item in a Solidity source file.
37#[derive(Debug)]
38pub struct Item<'ast> {
39    pub docs: DocComments<'ast>,
40    pub span: Span,
41    /// The item's kind.
42    pub kind: ItemKind<'ast>,
43}
44
45impl Item<'_> {
46    /// Returns the name of the item, if any.
47    pub fn name(&self) -> Option<Ident> {
48        self.kind.name()
49    }
50
51    /// Returns the description of the item.
52    pub fn description(&self) -> &'static str {
53        self.kind.description()
54    }
55
56    /// Returns `true` if the item is allowed inside of contracts.
57    pub fn is_allowed_in_contract(&self) -> bool {
58        self.kind.is_allowed_in_contract()
59    }
60}
61
62/// An AST item. A more expanded version of a [Solidity source unit][ref].
63///
64/// [ref]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.sourceUnit
65pub enum ItemKind<'ast> {
66    /// A pragma directive: `pragma solidity ^0.8.0;`
67    Pragma(PragmaDirective<'ast>),
68
69    /// An import directive: `import "foo.sol";`
70    Import(ImportDirective<'ast>),
71
72    /// A `using` directive: `using { A, B.add as + } for uint256 global;`
73    Using(UsingDirective<'ast>),
74
75    /// A contract, abstract contract, interface, or library definition:
76    /// `contract Foo is Bar, Baz { ... }`
77    Contract(ItemContract<'ast>),
78
79    /// A function, constructor, fallback, receive, or modifier definition:
80    /// `function helloWorld() external pure returns(string memory);`
81    Function(ItemFunction<'ast>),
82
83    /// A state variable or constant definition: `uint256 constant FOO = 42;`
84    Variable(VariableDefinition<'ast>),
85
86    /// A struct definition: `struct Foo { uint256 bar; }`
87    Struct(ItemStruct<'ast>),
88
89    /// An enum definition: `enum Foo { A, B, C }`
90    Enum(ItemEnum<'ast>),
91
92    /// A user-defined value type definition: `type Foo is uint256;`
93    Udvt(ItemUdvt<'ast>),
94
95    /// An error definition: `error Foo(uint256 a, uint256 b);`
96    Error(ItemError<'ast>),
97
98    /// An event definition:
99    /// `event Transfer(address indexed from, address indexed to, uint256 value);`
100    Event(ItemEvent<'ast>),
101}
102
103impl fmt::Debug for ItemKind<'_> {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.write_str("ItemKind::")?;
106        match self {
107            ItemKind::Pragma(item) => item.fmt(f),
108            ItemKind::Import(item) => item.fmt(f),
109            ItemKind::Using(item) => item.fmt(f),
110            ItemKind::Contract(item) => item.fmt(f),
111            ItemKind::Function(item) => item.fmt(f),
112            ItemKind::Variable(item) => item.fmt(f),
113            ItemKind::Struct(item) => item.fmt(f),
114            ItemKind::Enum(item) => item.fmt(f),
115            ItemKind::Udvt(item) => item.fmt(f),
116            ItemKind::Error(item) => item.fmt(f),
117            ItemKind::Event(item) => item.fmt(f),
118        }
119    }
120}
121
122impl ItemKind<'_> {
123    /// Returns the name of the item, if any.
124    pub fn name(&self) -> Option<Ident> {
125        match self {
126            Self::Pragma(_) | Self::Import(_) | Self::Using(_) => None,
127            Self::Contract(item) => Some(item.name),
128            Self::Function(item) => item.header.name,
129            Self::Variable(item) => item.name,
130            Self::Struct(item) => Some(item.name),
131            Self::Enum(item) => Some(item.name),
132            Self::Udvt(item) => Some(item.name),
133            Self::Error(item) => Some(item.name),
134            Self::Event(item) => Some(item.name),
135        }
136    }
137
138    /// Returns the description of the item.
139    pub fn description(&self) -> &'static str {
140        match self {
141            Self::Pragma(_) => "pragma directive",
142            Self::Import(_) => "import directive",
143            Self::Using(_) => "using directive",
144            Self::Contract(_) => "contract definition",
145            Self::Function(_) => "function definition",
146            Self::Variable(_) => "variable definition",
147            Self::Struct(_) => "struct definition",
148            Self::Enum(_) => "enum definition",
149            Self::Udvt(_) => "user-defined value type definition",
150            Self::Error(_) => "error definition",
151            Self::Event(_) => "event definition",
152        }
153    }
154
155    /// Returns `true` if the item is allowed inside of contracts.
156    pub fn is_allowed_in_contract(&self) -> bool {
157        match self {
158            Self::Pragma(_) => false,
159            Self::Import(_) => false,
160            Self::Using(_) => true,
161            Self::Contract(_) => false,
162            Self::Function(_) => true,
163            Self::Variable(_) => true,
164            Self::Struct(_) => true,
165            Self::Enum(_) => true,
166            Self::Udvt(_) => true,
167            Self::Error(_) => true,
168            Self::Event(_) => true,
169        }
170    }
171}
172
173/// A pragma directive: `pragma solidity ^0.8.0;`.
174#[derive(Debug)]
175pub struct PragmaDirective<'ast> {
176    /// The parsed or unparsed tokens of the pragma directive.
177    pub tokens: PragmaTokens<'ast>,
178}
179
180/// The parsed or unparsed tokens of a pragma directive.
181#[derive(Debug)]
182pub enum PragmaTokens<'ast> {
183    /// A Semantic Versioning requirement: `pragma solidity <req>;`.
184    ///
185    /// Note that this is parsed differently from the [`semver`] crate.
186    Version(Ident, SemverReq<'ast>),
187    /// `pragma <name> [value];`.
188    Custom(IdentOrStrLit, Option<IdentOrStrLit>),
189    /// Unparsed tokens: `pragma <tokens...>;`.
190    Verbatim(BoxSlice<'ast, Token>),
191}
192
193impl PragmaTokens<'_> {
194    /// Returns the name and value of the pragma directive, if any.
195    ///
196    /// # Examples
197    ///
198    /// ```solidity
199    /// pragma solidity ...;          // None
200    /// pragma abicoder v2;           // Some((Ident("abicoder"), Some(Ident("v2"))))
201    /// pragma experimental solidity; // Some((Ident("experimental"), Some(Ident("solidity"))))
202    /// pragma hello;                 // Some((Ident("hello"), None))
203    /// pragma hello world;           // Some((Ident("hello"), Some(Ident("world"))))
204    /// pragma hello "world";         // Some((Ident("hello"), Some(StrLit("world"))))
205    /// pragma "hello" world;         // Some((StrLit("hello"), Some(Ident("world"))))
206    /// pragma ???;                   // None
207    /// ```
208    pub fn as_name_and_value(&self) -> Option<(&IdentOrStrLit, Option<&IdentOrStrLit>)> {
209        match self {
210            Self::Custom(name, value) => Some((name, value.as_ref())),
211            _ => None,
212        }
213    }
214}
215
216/// An identifier or a string literal.
217///
218/// This is used in `pragma` declaration because Solc for some reason accepts and treats both as
219/// identical.
220///
221/// Parsed in: <https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L235>
222///
223/// Syntax-checked in: <https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/analysis/SyntaxChecker.cpp#L77>
224#[derive(Clone, Debug)]
225pub enum IdentOrStrLit {
226    /// An identifier.
227    Ident(Ident),
228    /// A string literal.
229    StrLit(StrLit),
230}
231
232impl IdentOrStrLit {
233    /// Returns the value of the identifier or literal.
234    pub fn value(&self) -> Symbol {
235        match self {
236            Self::Ident(ident) => ident.name,
237            Self::StrLit(str_lit) => str_lit.value,
238        }
239    }
240
241    /// Returns the string value of the identifier or literal.
242    pub fn as_str(&self) -> &str {
243        match self {
244            Self::Ident(ident) => ident.as_str(),
245            Self::StrLit(str_lit) => str_lit.value.as_str(),
246        }
247    }
248
249    /// Returns the span of the identifier or literal.
250    pub fn span(&self) -> Span {
251        match self {
252            Self::Ident(ident) => ident.span,
253            Self::StrLit(str_lit) => str_lit.span,
254        }
255    }
256}
257
258/// An import directive: `import "foo.sol";`.
259///
260/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.importDirective>
261#[derive(Debug)]
262pub struct ImportDirective<'ast> {
263    /// The path string literal value.
264    ///
265    /// Note that this is not escaped.
266    pub path: StrLit,
267    pub items: ImportItems<'ast>,
268}
269
270impl ImportDirective<'_> {
271    /// Returns the alias of the source, if any.
272    pub fn source_alias(&self) -> Option<Ident> {
273        self.items.source_alias()
274    }
275}
276
277/// The path of an import directive.
278#[derive(Debug)]
279pub enum ImportItems<'ast> {
280    /// A plain import directive: `import "foo.sol" as Foo;`.
281    Plain(Option<Ident>),
282    /// A list of import aliases: `import { Foo as Bar, Baz } from "foo.sol";`.
283    Aliases(BoxSlice<'ast, (Ident, Option<Ident>)>),
284    /// A glob import directive: `import * as Foo from "foo.sol";`.
285    Glob(Ident),
286}
287
288impl ImportItems<'_> {
289    /// Returns the alias of the source, if any.
290    pub fn source_alias(&self) -> Option<Ident> {
291        match *self {
292            ImportItems::Plain(ident) => ident,
293            ImportItems::Aliases(_) => None,
294            ImportItems::Glob(ident) => Some(ident),
295        }
296    }
297}
298
299/// A `using` directive: `using { A, B.add as + } for uint256 global;`.
300///
301/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.usingDirective>
302#[derive(Debug)]
303pub struct UsingDirective<'ast> {
304    /// The list of paths.
305    pub list: UsingList<'ast>,
306    /// The type for which this `using` directive applies. This is `*` if the value is `None`.
307    pub ty: Option<Type<'ast>>,
308    pub global: bool,
309}
310
311/// The path list of a `using` directive.
312#[derive(Debug)]
313pub enum UsingList<'ast> {
314    /// `A.B`
315    Single(AstPath<'ast>),
316    /// `{ A, B.add as + }`
317    Multiple(BoxSlice<'ast, (AstPath<'ast>, Option<UserDefinableOperator>)>),
318}
319
320/// A user-definable operator: `+`, `*`, `|`, etc.
321///
322/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.userDefinableOperator>
323#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
324pub enum UserDefinableOperator {
325    /// `&`
326    BitAnd,
327    /// `~`
328    BitNot,
329    /// `|`
330    BitOr,
331    /// `^`
332    BitXor,
333    /// `+`
334    Add,
335    /// `/`
336    Div,
337    /// `%`
338    Rem,
339    /// `*`
340    Mul,
341    /// `-`
342    Sub,
343    /// `==`
344    Eq,
345    /// `>=`
346    Ge,
347    /// `>`
348    Gt,
349    /// `<=`
350    Le,
351    /// `<`
352    Lt,
353    /// `!=`
354    Ne,
355}
356
357impl UserDefinableOperator {
358    /// Returns this operator as a binary or unary operator.
359    pub const fn to_op(self) -> Either<UnOpKind, BinOpKind> {
360        match self {
361            Self::BitAnd => Either::Right(BinOpKind::BitAnd),
362            Self::BitNot => Either::Left(UnOpKind::BitNot),
363            Self::BitOr => Either::Right(BinOpKind::BitOr),
364            Self::BitXor => Either::Right(BinOpKind::BitXor),
365            Self::Add => Either::Right(BinOpKind::Add),
366            Self::Div => Either::Right(BinOpKind::Div),
367            Self::Rem => Either::Right(BinOpKind::Rem),
368            Self::Mul => Either::Right(BinOpKind::Mul),
369            Self::Sub => Either::Right(BinOpKind::Sub),
370            Self::Eq => Either::Right(BinOpKind::Eq),
371            Self::Ge => Either::Right(BinOpKind::Ge),
372            Self::Gt => Either::Right(BinOpKind::Gt),
373            Self::Le => Either::Right(BinOpKind::Le),
374            Self::Lt => Either::Right(BinOpKind::Lt),
375            Self::Ne => Either::Right(BinOpKind::Ne),
376        }
377    }
378
379    /// Returns the string representation of the operator.
380    pub const fn to_str(self) -> &'static str {
381        match self.to_op() {
382            Either::Left(unop) => unop.to_str(),
383            Either::Right(binop) => binop.to_str(),
384        }
385    }
386}
387
388/// A contract, abstract contract, interface, or library definition:
389/// `contract Foo layout at 10 is Bar("foo"), Baz { ... }`.
390///
391/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.contractDefinition>
392#[derive(Debug)]
393pub struct ItemContract<'ast> {
394    pub kind: ContractKind,
395    pub name: Ident,
396    pub layout: Option<StorageLayoutSpecifier<'ast>>,
397    pub bases: BoxSlice<'ast, Modifier<'ast>>,
398    pub body: BoxSlice<'ast, Item<'ast>>,
399}
400
401/// The kind of contract.
402#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
403pub enum ContractKind {
404    /// `contract`
405    Contract,
406    /// `abstract contract`
407    AbstractContract,
408    /// `interface`
409    Interface,
410    /// `library`
411    Library,
412}
413
414impl fmt::Display for ContractKind {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        f.write_str(self.to_str())
417    }
418}
419
420impl ContractKind {
421    /// Returns the string representation of the contract kind.
422    pub const fn to_str(self) -> &'static str {
423        match self {
424            Self::Contract => "contract",
425            Self::AbstractContract => "abstract contract",
426            Self::Interface => "interface",
427            Self::Library => "library",
428        }
429    }
430}
431
432/// The storage layout specifier of a contract.
433///
434/// Reference: <https://docs.soliditylang.org/en/latest/contracts.html#custom-storage-layout>
435#[derive(Debug)]
436pub struct StorageLayoutSpecifier<'ast> {
437    pub span: Span,
438    pub slot: Box<'ast, Expr<'ast>>,
439}
440
441/// A function, constructor, fallback, receive, or modifier definition:
442/// `function helloWorld() external pure returns(string memory);`.
443///
444/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.functionDefinition>
445#[derive(Debug)]
446pub struct ItemFunction<'ast> {
447    /// What kind of function this is.
448    pub kind: FunctionKind,
449    /// The function header.
450    pub header: FunctionHeader<'ast>,
451    /// The body of the function. This is `;` when the value is `None`.
452    pub body: Option<Block<'ast>>,
453    /// The span of the body. Points to the `;` if the function is not implemented.
454    pub body_span: Span,
455}
456
457impl ItemFunction<'_> {
458    /// Returns `true` if the function is implemented.
459    pub fn is_implemented(&self) -> bool {
460        self.body.is_some()
461    }
462}
463
464/// A function header: `function helloWorld() external pure returns(string memory)`.
465#[derive(Debug, Default)]
466pub struct FunctionHeader<'ast> {
467    /// The span of the function header.
468    pub span: Span,
469
470    /// The name of the function.
471    /// Only `None` if this is a constructor, fallback, or receive function.
472    pub name: Option<Ident>,
473
474    /// The parameters of the function.
475    pub parameters: ParameterList<'ast>,
476
477    /// The visibility keyword.
478    pub visibility: Option<Spanned<Visibility>>,
479
480    /// The state mutability.
481    pub state_mutability: Option<Spanned<StateMutability>>,
482
483    /// The function modifiers.
484    pub modifiers: BoxSlice<'ast, Modifier<'ast>>,
485
486    /// The span of the `virtual` keyword.
487    pub virtual_: Option<Span>,
488
489    /// The `override` keyword.
490    pub override_: Option<Override<'ast>>,
491
492    /// The returns parameter list.
493    ///
494    /// If `Some`, it's always non-empty.
495    pub returns: Option<ParameterList<'ast>>,
496}
497
498impl<'ast> FunctionHeader<'ast> {
499    pub fn visibility(&self) -> Option<Visibility> {
500        self.visibility.map(Spanned::into_inner)
501    }
502
503    pub fn state_mutability(&self) -> StateMutability {
504        self.state_mutability.map(Spanned::into_inner).unwrap_or(StateMutability::NonPayable)
505    }
506
507    pub fn virtual_(&self) -> bool {
508        self.virtual_.is_some()
509    }
510
511    pub fn returns(&self) -> &[VariableDefinition<'ast>] {
512        self.returns.as_ref().map(|pl| &pl.vars[..]).unwrap_or(&[])
513    }
514}
515
516/// A kind of function.
517#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
518pub enum FunctionKind {
519    /// `constructor`
520    Constructor,
521    /// `function`
522    Function,
523    /// `fallback`
524    Fallback,
525    /// `receive`
526    Receive,
527    /// `modifier`
528    Modifier,
529}
530
531impl fmt::Display for FunctionKind {
532    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533        f.write_str(self.to_str())
534    }
535}
536
537impl FunctionKind {
538    /// Returns the string representation of the function kind.
539    pub const fn to_str(self) -> &'static str {
540        match self {
541            Self::Constructor => "constructor",
542            Self::Function => "function",
543            Self::Fallback => "fallback",
544            Self::Receive => "receive",
545            Self::Modifier => "modifier",
546        }
547    }
548
549    /// Returns `true` if the function is allowed in global scope.
550    pub fn allowed_in_global(&self) -> bool {
551        self.is_ordinary()
552    }
553
554    /// Returns `true` if the function is an ordinary function.
555    pub fn is_ordinary(&self) -> bool {
556        matches!(self, Self::Function)
557    }
558}
559
560/// A [modifier invocation][m], or an [inheritance specifier][i].
561///
562/// [m]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.modifierInvocation
563/// [i]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.inheritanceSpecifier
564#[derive(Debug)]
565pub struct Modifier<'ast> {
566    pub name: AstPath<'ast>,
567    pub arguments: CallArgs<'ast>,
568}
569
570impl Modifier<'_> {
571    /// Returns the span of the modifier.
572    pub fn span(&self) -> Span {
573        self.name.span().to(self.arguments.span)
574    }
575}
576
577/// An override specifier: `override`, `override(a, b.c)`.
578#[derive(Debug)]
579pub struct Override<'ast> {
580    pub span: Span,
581    pub paths: BoxSlice<'ast, AstPath<'ast>>,
582}
583
584/// A storage location.
585#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
586pub enum DataLocation {
587    /// `storage`
588    Storage,
589    /// `transient`
590    Transient,
591    /// `memory`
592    Memory,
593    /// `calldata`
594    Calldata,
595}
596
597impl fmt::Display for DataLocation {
598    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599        f.write_str(self.to_str())
600    }
601}
602
603impl DataLocation {
604    /// Returns the string representation of the storage location.
605    pub const fn to_str(self) -> &'static str {
606        match self {
607            Self::Storage => "storage",
608            Self::Transient => "transient",
609            Self::Memory => "memory",
610            Self::Calldata => "calldata",
611        }
612    }
613
614    /// Returns the string representation of the storage location, or `"none"` if `None`.
615    pub const fn opt_to_str(this: Option<Self>) -> &'static str {
616        match this {
617            Some(location) => location.to_str(),
618            None => "none",
619        }
620    }
621}
622
623// How a function can mutate the EVM state.
624#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, EnumIs, PartialOrd, Ord)]
625pub enum StateMutability {
626    /// `pure`
627    Pure,
628    /// `view`
629    View,
630    /// `payable`
631    Payable,
632    /// Not specified.
633    #[default]
634    NonPayable,
635}
636
637impl fmt::Display for StateMutability {
638    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
639        f.write_str(self.to_str())
640    }
641}
642
643impl StateMutability {
644    /// Returns the string representation of the state mutability.
645    pub const fn to_str(self) -> &'static str {
646        match self {
647            Self::Pure => "pure",
648            Self::View => "view",
649            Self::Payable => "payable",
650            Self::NonPayable => "nonpayable",
651        }
652    }
653}
654
655/// Visibility ordered from restricted to unrestricted.
656#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
657pub enum Visibility {
658    /// `private`: visible only in the current contract.
659    Private,
660    /// `internal`: visible only in the current contract and contracts deriving from it.
661    Internal,
662    /// `public`: visible internally and externally.
663    Public,
664    /// `external`: visible only externally.
665    External,
666}
667
668impl fmt::Display for Visibility {
669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670        self.to_str().fmt(f)
671    }
672}
673
674impl Visibility {
675    /// Returns the string representation of the visibility.
676    pub const fn to_str(self) -> &'static str {
677        match self {
678            Self::Private => "private",
679            Self::Internal => "internal",
680            Self::Public => "public",
681            Self::External => "external",
682        }
683    }
684}
685
686/// A state variable or constant definition: `uint256 constant FOO = 42;`.
687///
688/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.stateVariableDeclaration>
689#[derive(Debug)]
690pub struct VariableDefinition<'ast> {
691    pub span: Span,
692    pub ty: Type<'ast>,
693    pub visibility: Option<Visibility>,
694    pub mutability: Option<VarMut>,
695    pub data_location: Option<DataLocation>,
696    pub override_: Option<Override<'ast>>,
697    pub indexed: bool,
698    pub name: Option<Ident>,
699    pub initializer: Option<Box<'ast, Expr<'ast>>>,
700}
701
702/// The mutability of a variable.
703#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
704pub enum VarMut {
705    /// `immutable`
706    Immutable,
707    /// `constant`
708    Constant,
709}
710
711impl fmt::Display for VarMut {
712    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713        f.write_str(self.to_str())
714    }
715}
716
717impl VarMut {
718    /// Returns the string representation of the variable mutability.
719    pub const fn to_str(self) -> &'static str {
720        match self {
721            Self::Immutable => "immutable",
722            Self::Constant => "constant",
723        }
724    }
725
726    /// Returns `true` if the variable is immutable.
727    pub const fn is_immutable(self) -> bool {
728        matches!(self, Self::Immutable)
729    }
730
731    /// Returns `true` if the variable is constant.
732    pub const fn is_constant(self) -> bool {
733        matches!(self, Self::Constant)
734    }
735}
736
737/// A struct definition: `struct Foo { uint256 bar; }`.
738///
739/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.structDefinition>
740#[derive(Debug)]
741pub struct ItemStruct<'ast> {
742    pub name: Ident,
743    pub fields: BoxSlice<'ast, VariableDefinition<'ast>>,
744}
745
746/// An enum definition: `enum Foo { A, B, C }`.
747///
748/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.enumDefinition>
749#[derive(Debug)]
750pub struct ItemEnum<'ast> {
751    pub name: Ident,
752    pub variants: BoxSlice<'ast, Ident>,
753}
754
755/// A user-defined value type definition: `type Foo is uint256;`.
756///
757/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.userDefinedValueTypeDefinition>
758#[derive(Debug)]
759pub struct ItemUdvt<'ast> {
760    pub name: Ident,
761    pub ty: Type<'ast>,
762}
763
764/// An error definition: `error Foo(uint256 a, uint256 b);`.
765///
766/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.errorDefinition>
767#[derive(Debug)]
768pub struct ItemError<'ast> {
769    pub name: Ident,
770    pub parameters: ParameterList<'ast>,
771}
772
773/// An event definition:
774/// `event Transfer(address indexed from, address indexed to, uint256 value);`.
775///
776/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.eventDefinition>
777#[derive(Debug)]
778pub struct ItemEvent<'ast> {
779    pub name: Ident,
780    pub parameters: ParameterList<'ast>,
781    pub anonymous: bool,
782}