solar_ast/ast/
item.rs

1use super::{
2    AstPath, BinOpKind, Block, Box, CallArgs, DocComments, Expr, SemverReq, StrLit, Type, UnOpKind,
3};
4use crate::token::Token;
5use either::Either;
6use solar_interface::{Ident, Span, Spanned};
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: Box<'ast, [VariableDefinition<'ast>]>,
20}
21
22impl<'ast> Deref for ParameterList<'ast> {
23    type Target = Box<'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(Box<'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/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L235>
222///
223/// Syntax-checked in: <https://github.com/ethereum/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 string value of the identifier or literal.
234    pub fn as_str(&self) -> &str {
235        match self {
236            Self::Ident(ident) => ident.as_str(),
237            Self::StrLit(str_lit) => str_lit.value.as_str(),
238        }
239    }
240
241    /// Returns the span of the identifier or literal.
242    pub fn span(&self) -> Span {
243        match self {
244            Self::Ident(ident) => ident.span,
245            Self::StrLit(str_lit) => str_lit.span,
246        }
247    }
248}
249
250/// An import directive: `import "foo.sol";`.
251///
252/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.importDirective>
253#[derive(Debug)]
254pub struct ImportDirective<'ast> {
255    /// The path string literal value.
256    ///
257    /// Note that this is not escaped.
258    pub path: StrLit,
259    pub items: ImportItems<'ast>,
260}
261
262impl ImportDirective<'_> {
263    /// Returns the alias of the source, if any.
264    pub fn source_alias(&self) -> Option<Ident> {
265        self.items.source_alias()
266    }
267}
268
269/// The path of an import directive.
270#[derive(Debug)]
271pub enum ImportItems<'ast> {
272    /// A plain import directive: `import "foo.sol" as Foo;`.
273    Plain(Option<Ident>),
274    /// A list of import aliases: `import { Foo as Bar, Baz } from "foo.sol";`.
275    Aliases(Box<'ast, [(Ident, Option<Ident>)]>),
276    /// A glob import directive: `import * as Foo from "foo.sol";`.
277    Glob(Ident),
278}
279
280impl ImportItems<'_> {
281    /// Returns the alias of the source, if any.
282    pub fn source_alias(&self) -> Option<Ident> {
283        match *self {
284            ImportItems::Plain(ident) => ident,
285            ImportItems::Aliases(_) => None,
286            ImportItems::Glob(ident) => Some(ident),
287        }
288    }
289}
290
291/// A `using` directive: `using { A, B.add as + } for uint256 global;`.
292///
293/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.usingDirective>
294#[derive(Debug)]
295pub struct UsingDirective<'ast> {
296    /// The list of paths.
297    pub list: UsingList<'ast>,
298    /// The type for which this `using` directive applies. This is `*` if the value is `None`.
299    pub ty: Option<Type<'ast>>,
300    pub global: bool,
301}
302
303/// The path list of a `using` directive.
304#[derive(Debug)]
305pub enum UsingList<'ast> {
306    /// `A.B`
307    Single(AstPath<'ast>),
308    /// `{ A, B.add as + }`
309    Multiple(Box<'ast, [(AstPath<'ast>, Option<UserDefinableOperator>)]>),
310}
311
312/// A user-definable operator: `+`, `*`, `|`, etc.
313///
314/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.userDefinableOperator>
315#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
316pub enum UserDefinableOperator {
317    /// `&`
318    BitAnd,
319    /// `~`
320    BitNot,
321    /// `|`
322    BitOr,
323    /// `^`
324    BitXor,
325    /// `+`
326    Add,
327    /// `/`
328    Div,
329    /// `%`
330    Rem,
331    /// `*`
332    Mul,
333    /// `-`
334    Sub,
335    /// `==`
336    Eq,
337    /// `>=`
338    Ge,
339    /// `>`
340    Gt,
341    /// `<=`
342    Le,
343    /// `<`
344    Lt,
345    /// `!=`
346    Ne,
347}
348
349impl UserDefinableOperator {
350    /// Returns this operator as a binary or unary operator.
351    pub const fn to_op(self) -> Either<UnOpKind, BinOpKind> {
352        match self {
353            Self::BitAnd => Either::Right(BinOpKind::BitAnd),
354            Self::BitNot => Either::Left(UnOpKind::BitNot),
355            Self::BitOr => Either::Right(BinOpKind::BitOr),
356            Self::BitXor => Either::Right(BinOpKind::BitXor),
357            Self::Add => Either::Right(BinOpKind::Add),
358            Self::Div => Either::Right(BinOpKind::Div),
359            Self::Rem => Either::Right(BinOpKind::Rem),
360            Self::Mul => Either::Right(BinOpKind::Mul),
361            Self::Sub => Either::Right(BinOpKind::Sub),
362            Self::Eq => Either::Right(BinOpKind::Eq),
363            Self::Ge => Either::Right(BinOpKind::Ge),
364            Self::Gt => Either::Right(BinOpKind::Gt),
365            Self::Le => Either::Right(BinOpKind::Le),
366            Self::Lt => Either::Right(BinOpKind::Lt),
367            Self::Ne => Either::Right(BinOpKind::Ne),
368        }
369    }
370
371    /// Returns the string representation of the operator.
372    pub const fn to_str(self) -> &'static str {
373        match self.to_op() {
374            Either::Left(unop) => unop.to_str(),
375            Either::Right(binop) => binop.to_str(),
376        }
377    }
378}
379
380/// A contract, abstract contract, interface, or library definition:
381/// `contract Foo layout at 10 is Bar("foo"), Baz { ... }`.
382///
383/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.contractDefinition>
384#[derive(Debug)]
385pub struct ItemContract<'ast> {
386    pub kind: ContractKind,
387    pub name: Ident,
388    pub layout: Option<StorageLayoutSpecifier<'ast>>,
389    pub bases: Box<'ast, [Modifier<'ast>]>,
390    pub body: Box<'ast, [Item<'ast>]>,
391}
392
393/// The kind of contract.
394#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
395pub enum ContractKind {
396    /// `contract`
397    Contract,
398    /// `abstract contract`
399    AbstractContract,
400    /// `interface`
401    Interface,
402    /// `library`
403    Library,
404}
405
406impl fmt::Display for ContractKind {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        f.write_str(self.to_str())
409    }
410}
411
412impl ContractKind {
413    /// Returns the string representation of the contract kind.
414    pub const fn to_str(self) -> &'static str {
415        match self {
416            Self::Contract => "contract",
417            Self::AbstractContract => "abstract contract",
418            Self::Interface => "interface",
419            Self::Library => "library",
420        }
421    }
422}
423
424/// The storage layout specifier of a contract.
425///
426/// Reference: <https://docs.soliditylang.org/en/latest/contracts.html#custom-storage-layout>
427#[derive(Debug)]
428pub struct StorageLayoutSpecifier<'ast> {
429    pub span: Span,
430    pub slot: Box<'ast, Expr<'ast>>,
431}
432
433/// A function, constructor, fallback, receive, or modifier definition:
434/// `function helloWorld() external pure returns(string memory);`.
435///
436/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.functionDefinition>
437#[derive(Debug)]
438pub struct ItemFunction<'ast> {
439    /// What kind of function this is.
440    pub kind: FunctionKind,
441    /// The function header.
442    pub header: FunctionHeader<'ast>,
443    /// The body of the function. This is `;` when the value is `None`.
444    pub body: Option<Block<'ast>>,
445    /// The span of the body. Points to the `;` if the function is not implemented.
446    pub body_span: Span,
447}
448
449impl ItemFunction<'_> {
450    /// Returns `true` if the function is implemented.
451    pub fn is_implemented(&self) -> bool {
452        self.body.is_some()
453    }
454}
455
456/// A function header: `function helloWorld() external pure returns(string memory)`.
457#[derive(Debug, Default)]
458pub struct FunctionHeader<'ast> {
459    /// The span of the function header.
460    pub span: Span,
461
462    /// The name of the function.
463    /// Only `None` if this is a constructor, fallback, or receive function.
464    pub name: Option<Ident>,
465
466    /// The parameters of the function.
467    pub parameters: ParameterList<'ast>,
468
469    /// The visibility keyword.
470    pub visibility: Option<Spanned<Visibility>>,
471
472    /// The state mutability.
473    pub state_mutability: Option<Spanned<StateMutability>>,
474
475    /// The function modifiers.
476    pub modifiers: Box<'ast, [Modifier<'ast>]>,
477
478    /// The span of the `virtual` keyword.
479    pub virtual_: Option<Span>,
480
481    /// The `override` keyword.
482    pub override_: Option<Override<'ast>>,
483
484    /// The returns parameter list.
485    ///
486    /// If `Some`, it's always non-empty.
487    pub returns: Option<ParameterList<'ast>>,
488}
489
490impl<'ast> FunctionHeader<'ast> {
491    pub fn visibility(&self) -> Option<Visibility> {
492        self.visibility.map(Spanned::into_inner)
493    }
494
495    pub fn state_mutability(&self) -> StateMutability {
496        self.state_mutability.map(Spanned::into_inner).unwrap_or(StateMutability::NonPayable)
497    }
498
499    pub fn virtual_(&self) -> bool {
500        self.virtual_.is_some()
501    }
502
503    pub fn returns(&self) -> &[VariableDefinition<'ast>] {
504        self.returns.as_ref().map(|pl| &pl.vars[..]).unwrap_or(&[])
505    }
506}
507
508/// A kind of function.
509#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
510pub enum FunctionKind {
511    /// `constructor`
512    Constructor,
513    /// `function`
514    Function,
515    /// `fallback`
516    Fallback,
517    /// `receive`
518    Receive,
519    /// `modifier`
520    Modifier,
521}
522
523impl fmt::Display for FunctionKind {
524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525        f.write_str(self.to_str())
526    }
527}
528
529impl FunctionKind {
530    /// Returns the string representation of the function kind.
531    pub const fn to_str(self) -> &'static str {
532        match self {
533            Self::Constructor => "constructor",
534            Self::Function => "function",
535            Self::Fallback => "fallback",
536            Self::Receive => "receive",
537            Self::Modifier => "modifier",
538        }
539    }
540
541    /// Returns `true` if the function is allowed in global scope.
542    pub fn allowed_in_global(&self) -> bool {
543        self.is_ordinary()
544    }
545
546    /// Returns `true` if the function is an ordinary function.
547    pub fn is_ordinary(&self) -> bool {
548        matches!(self, Self::Function)
549    }
550}
551
552/// A [modifier invocation][m], or an [inheritance specifier][i].
553///
554/// [m]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.modifierInvocation
555/// [i]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.inheritanceSpecifier
556#[derive(Debug)]
557pub struct Modifier<'ast> {
558    pub name: AstPath<'ast>,
559    pub arguments: CallArgs<'ast>,
560}
561
562impl Modifier<'_> {
563    /// Returns the span of the modifier.
564    pub fn span(&self) -> Span {
565        self.name.span().to(self.arguments.span)
566    }
567}
568
569/// An override specifier: `override`, `override(a, b.c)`.
570#[derive(Debug)]
571pub struct Override<'ast> {
572    pub span: Span,
573    pub paths: Box<'ast, [AstPath<'ast>]>,
574}
575
576/// A storage location.
577#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
578pub enum DataLocation {
579    /// `storage`
580    Storage,
581    /// `transient`
582    Transient,
583    /// `memory`
584    Memory,
585    /// `calldata`
586    Calldata,
587}
588
589impl fmt::Display for DataLocation {
590    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591        f.write_str(self.to_str())
592    }
593}
594
595impl DataLocation {
596    /// Returns the string representation of the storage location.
597    pub const fn to_str(self) -> &'static str {
598        match self {
599            Self::Storage => "storage",
600            Self::Transient => "transient",
601            Self::Memory => "memory",
602            Self::Calldata => "calldata",
603        }
604    }
605
606    /// Returns the string representation of the storage location, or `"none"` if `None`.
607    pub const fn opt_to_str(this: Option<Self>) -> &'static str {
608        match this {
609            Some(location) => location.to_str(),
610            None => "none",
611        }
612    }
613}
614
615// How a function can mutate the EVM state.
616#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, EnumIs, PartialOrd, Ord)]
617pub enum StateMutability {
618    /// `pure`
619    Pure,
620    /// `view`
621    View,
622    /// `payable`
623    Payable,
624    /// Not specified.
625    #[default]
626    NonPayable,
627}
628
629impl fmt::Display for StateMutability {
630    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631        f.write_str(self.to_str())
632    }
633}
634
635impl StateMutability {
636    /// Returns the string representation of the state mutability.
637    pub const fn to_str(self) -> &'static str {
638        match self {
639            Self::Pure => "pure",
640            Self::View => "view",
641            Self::Payable => "payable",
642            Self::NonPayable => "nonpayable",
643        }
644    }
645}
646
647/// Visibility ordered from restricted to unrestricted.
648#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
649pub enum Visibility {
650    /// `private`: visible only in the current contract.
651    Private,
652    /// `internal`: visible only in the current contract and contracts deriving from it.
653    Internal,
654    /// `public`: visible internally and externally.
655    Public,
656    /// `external`: visible only externally.
657    External,
658}
659
660impl fmt::Display for Visibility {
661    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662        self.to_str().fmt(f)
663    }
664}
665
666impl Visibility {
667    /// Returns the string representation of the visibility.
668    pub const fn to_str(self) -> &'static str {
669        match self {
670            Self::Private => "private",
671            Self::Internal => "internal",
672            Self::Public => "public",
673            Self::External => "external",
674        }
675    }
676}
677
678/// A state variable or constant definition: `uint256 constant FOO = 42;`.
679///
680/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.stateVariableDeclaration>
681#[derive(Debug)]
682pub struct VariableDefinition<'ast> {
683    pub span: Span,
684    pub ty: Type<'ast>,
685    pub visibility: Option<Visibility>,
686    pub mutability: Option<VarMut>,
687    pub data_location: Option<DataLocation>,
688    pub override_: Option<Override<'ast>>,
689    pub indexed: bool,
690    pub name: Option<Ident>,
691    pub initializer: Option<Box<'ast, Expr<'ast>>>,
692}
693
694/// The mutability of a variable.
695#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
696pub enum VarMut {
697    /// `immutable`
698    Immutable,
699    /// `constant`
700    Constant,
701}
702
703impl fmt::Display for VarMut {
704    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705        f.write_str(self.to_str())
706    }
707}
708
709impl VarMut {
710    /// Returns the string representation of the variable mutability.
711    pub const fn to_str(self) -> &'static str {
712        match self {
713            Self::Immutable => "immutable",
714            Self::Constant => "constant",
715        }
716    }
717
718    /// Returns `true` if the variable is immutable.
719    pub const fn is_immutable(self) -> bool {
720        matches!(self, Self::Immutable)
721    }
722
723    /// Returns `true` if the variable is constant.
724    pub const fn is_constant(self) -> bool {
725        matches!(self, Self::Constant)
726    }
727}
728
729/// A struct definition: `struct Foo { uint256 bar; }`.
730///
731/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.structDefinition>
732#[derive(Debug)]
733pub struct ItemStruct<'ast> {
734    pub name: Ident,
735    pub fields: Box<'ast, [VariableDefinition<'ast>]>,
736}
737
738/// An enum definition: `enum Foo { A, B, C }`.
739///
740/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.enumDefinition>
741#[derive(Debug)]
742pub struct ItemEnum<'ast> {
743    pub name: Ident,
744    pub variants: Box<'ast, [Ident]>,
745}
746
747/// A user-defined value type definition: `type Foo is uint256;`.
748///
749/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.userDefinedValueTypeDefinition>
750#[derive(Debug)]
751pub struct ItemUdvt<'ast> {
752    pub name: Ident,
753    pub ty: Type<'ast>,
754}
755
756/// An error definition: `error Foo(uint256 a, uint256 b);`.
757///
758/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.errorDefinition>
759#[derive(Debug)]
760pub struct ItemError<'ast> {
761    pub name: Ident,
762    pub parameters: ParameterList<'ast>,
763}
764
765/// An event definition:
766/// `event Transfer(address indexed from, address indexed to, uint256 value);`.
767///
768/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.eventDefinition>
769#[derive(Debug)]
770pub struct ItemEvent<'ast> {
771    pub name: Ident,
772    pub parameters: ParameterList<'ast>,
773    pub anonymous: bool,
774}