Skip to main content

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 the user-definable operator for a unary operator, if any.
359    pub const fn from_unop(op: UnOpKind) -> Option<Self> {
360        Some(match op {
361            UnOpKind::Neg => Self::Sub,
362            UnOpKind::BitNot => Self::BitNot,
363            UnOpKind::PreInc
364            | UnOpKind::PreDec
365            | UnOpKind::Not
366            | UnOpKind::PostInc
367            | UnOpKind::PostDec => {
368                return None;
369            }
370        })
371    }
372
373    /// Returns the user-definable operator for a binary operator, if any.
374    pub const fn from_binop(op: BinOpKind) -> Option<Self> {
375        Some(match op {
376            BinOpKind::BitAnd => Self::BitAnd,
377            BinOpKind::BitOr => Self::BitOr,
378            BinOpKind::BitXor => Self::BitXor,
379            BinOpKind::Add => Self::Add,
380            BinOpKind::Div => Self::Div,
381            BinOpKind::Rem => Self::Rem,
382            BinOpKind::Mul => Self::Mul,
383            BinOpKind::Sub => Self::Sub,
384            BinOpKind::Eq => Self::Eq,
385            BinOpKind::Ge => Self::Ge,
386            BinOpKind::Gt => Self::Gt,
387            BinOpKind::Le => Self::Le,
388            BinOpKind::Lt => Self::Lt,
389            BinOpKind::Ne => Self::Ne,
390            BinOpKind::Or
391            | BinOpKind::And
392            | BinOpKind::Shr
393            | BinOpKind::Shl
394            | BinOpKind::Sar
395            | BinOpKind::Pow => {
396                return None;
397            }
398        })
399    }
400
401    /// Returns this operator as a binary or unary operator.
402    pub const fn to_op(self) -> Either<UnOpKind, BinOpKind> {
403        match self {
404            Self::BitAnd => Either::Right(BinOpKind::BitAnd),
405            Self::BitNot => Either::Left(UnOpKind::BitNot),
406            Self::BitOr => Either::Right(BinOpKind::BitOr),
407            Self::BitXor => Either::Right(BinOpKind::BitXor),
408            Self::Add => Either::Right(BinOpKind::Add),
409            Self::Div => Either::Right(BinOpKind::Div),
410            Self::Rem => Either::Right(BinOpKind::Rem),
411            Self::Mul => Either::Right(BinOpKind::Mul),
412            Self::Sub => Either::Right(BinOpKind::Sub),
413            Self::Eq => Either::Right(BinOpKind::Eq),
414            Self::Ge => Either::Right(BinOpKind::Ge),
415            Self::Gt => Either::Right(BinOpKind::Gt),
416            Self::Le => Either::Right(BinOpKind::Le),
417            Self::Lt => Either::Right(BinOpKind::Lt),
418            Self::Ne => Either::Right(BinOpKind::Ne),
419        }
420    }
421
422    /// Returns the string representation of the operator.
423    pub const fn to_str(self) -> &'static str {
424        match self.to_op() {
425            Either::Left(unop) => unop.to_str(),
426            Either::Right(binop) => binop.to_str(),
427        }
428    }
429}
430
431/// A contract, abstract contract, interface, or library definition:
432/// `contract Foo layout at 10 is Bar("foo"), Baz { ... }`.
433///
434/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.contractDefinition>
435#[derive(Debug)]
436pub struct ItemContract<'ast> {
437    pub kind: ContractKind,
438    pub name: Ident,
439    pub layout: Option<StorageLayoutSpecifier<'ast>>,
440    pub bases: BoxSlice<'ast, Modifier<'ast>>,
441    pub body: BoxSlice<'ast, Item<'ast>>,
442}
443
444/// The kind of contract.
445#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
446pub enum ContractKind {
447    /// `contract`
448    Contract,
449    /// `abstract contract`
450    AbstractContract,
451    /// `interface`
452    Interface,
453    /// `library`
454    Library,
455}
456
457impl fmt::Display for ContractKind {
458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459        f.write_str(self.to_str())
460    }
461}
462
463impl ContractKind {
464    /// Returns the string representation of the contract kind.
465    pub const fn to_str(self) -> &'static str {
466        match self {
467            Self::Contract => "contract",
468            Self::AbstractContract => "abstract contract",
469            Self::Interface => "interface",
470            Self::Library => "library",
471        }
472    }
473}
474
475/// The storage layout specifier of a contract.
476///
477/// Reference: <https://docs.soliditylang.org/en/latest/contracts.html#custom-storage-layout>
478#[derive(Debug)]
479pub struct StorageLayoutSpecifier<'ast> {
480    pub span: Span,
481    pub slot: Box<'ast, Expr<'ast>>,
482}
483
484/// A function, constructor, fallback, receive, or modifier definition:
485/// `function helloWorld() external pure returns(string memory);`.
486///
487/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.functionDefinition>
488#[derive(Debug)]
489pub struct ItemFunction<'ast> {
490    /// What kind of function this is.
491    pub kind: FunctionKind,
492    /// The function header.
493    pub header: FunctionHeader<'ast>,
494    /// The body of the function. This is `;` when the value is `None`.
495    pub body: Option<Block<'ast>>,
496    /// The span of the body. Points to the `;` if the function is not implemented.
497    pub body_span: Span,
498}
499
500impl ItemFunction<'_> {
501    /// Returns `true` if the function is implemented.
502    pub fn is_implemented(&self) -> bool {
503        self.body.is_some()
504    }
505}
506
507/// A function header: `function helloWorld() external pure returns(string memory)`.
508#[derive(Debug, Default)]
509pub struct FunctionHeader<'ast> {
510    /// The span of the function header.
511    pub span: Span,
512
513    /// The name of the function.
514    /// Only `None` if this is a constructor, fallback, or receive function.
515    pub name: Option<Ident>,
516
517    /// The parameters of the function.
518    pub parameters: ParameterList<'ast>,
519
520    /// The visibility keyword.
521    pub visibility: Option<Spanned<Visibility>>,
522
523    /// The state mutability.
524    pub state_mutability: Option<Spanned<StateMutability>>,
525
526    /// The function modifiers.
527    pub modifiers: BoxSlice<'ast, Modifier<'ast>>,
528
529    /// The span of the `virtual` keyword.
530    pub virtual_: Option<Span>,
531
532    /// The `override` keyword.
533    pub override_: Option<Override<'ast>>,
534
535    /// The returns parameter list.
536    ///
537    /// If `Some`, it's always non-empty.
538    pub returns: Option<ParameterList<'ast>>,
539}
540
541impl<'ast> FunctionHeader<'ast> {
542    pub fn visibility(&self) -> Option<Visibility> {
543        self.visibility.map(Spanned::into_inner)
544    }
545
546    pub fn state_mutability(&self) -> StateMutability {
547        self.state_mutability.map(Spanned::into_inner).unwrap_or(StateMutability::NonPayable)
548    }
549
550    pub fn virtual_(&self) -> bool {
551        self.virtual_.is_some()
552    }
553
554    pub fn returns(&self) -> &[VariableDefinition<'ast>] {
555        self.returns.as_ref().map(|pl| &pl.vars[..]).unwrap_or(&[])
556    }
557}
558
559/// A kind of function.
560#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
561pub enum FunctionKind {
562    /// `constructor`
563    Constructor,
564    /// `function`
565    Function,
566    /// `fallback`
567    Fallback,
568    /// `receive`
569    Receive,
570    /// `modifier`
571    Modifier,
572}
573
574impl fmt::Display for FunctionKind {
575    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576        f.write_str(self.to_str())
577    }
578}
579
580impl FunctionKind {
581    /// Returns the string representation of the function kind.
582    pub const fn to_str(self) -> &'static str {
583        match self {
584            Self::Constructor => "constructor",
585            Self::Function => "function",
586            Self::Fallback => "fallback",
587            Self::Receive => "receive",
588            Self::Modifier => "modifier",
589        }
590    }
591
592    /// Returns `true` if the function is allowed in global scope.
593    pub fn allowed_in_global(&self) -> bool {
594        self.is_ordinary()
595    }
596
597    /// Returns `true` if the function is an ordinary function.
598    pub fn is_ordinary(&self) -> bool {
599        matches!(self, Self::Function)
600    }
601}
602
603/// A [modifier invocation][m], or an [inheritance specifier][i].
604///
605/// [m]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.modifierInvocation
606/// [i]: https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.inheritanceSpecifier
607#[derive(Debug)]
608pub struct Modifier<'ast> {
609    pub name: AstPath<'ast>,
610    pub arguments: CallArgs<'ast>,
611}
612
613impl Modifier<'_> {
614    /// Returns the span of the modifier.
615    pub fn span(&self) -> Span {
616        self.name.span().to(self.arguments.span)
617    }
618}
619
620/// An override specifier: `override`, `override(a, b.c)`.
621#[derive(Debug)]
622pub struct Override<'ast> {
623    pub span: Span,
624    pub paths: BoxSlice<'ast, AstPath<'ast>>,
625}
626
627/// A storage location.
628#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
629pub enum DataLocation {
630    /// `storage`
631    Storage,
632    /// `transient`
633    Transient,
634    /// `memory`
635    Memory,
636    /// `calldata`
637    Calldata,
638}
639
640impl fmt::Display for DataLocation {
641    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642        f.write_str(self.to_str())
643    }
644}
645
646impl DataLocation {
647    /// Returns the string representation of the storage location.
648    pub const fn to_str(self) -> &'static str {
649        match self {
650            Self::Storage => "storage",
651            Self::Transient => "transient",
652            Self::Memory => "memory",
653            Self::Calldata => "calldata",
654        }
655    }
656
657    /// Returns the string representation of the storage location, or `"none"` if `None`.
658    pub const fn opt_to_str(this: Option<Self>) -> &'static str {
659        match this {
660            Some(location) => location.to_str(),
661            None => "none",
662        }
663    }
664}
665
666// How a function can mutate the EVM state.
667#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, EnumIs, PartialOrd, Ord)]
668pub enum StateMutability {
669    /// `pure`
670    Pure,
671    /// `view`
672    View,
673    /// `payable`
674    Payable,
675    /// Not specified.
676    #[default]
677    NonPayable,
678}
679
680impl fmt::Display for StateMutability {
681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682        f.write_str(self.to_str())
683    }
684}
685
686impl StateMutability {
687    /// Returns the string representation of the state mutability.
688    pub const fn to_str(self) -> &'static str {
689        match self {
690            Self::Pure => "pure",
691            Self::View => "view",
692            Self::Payable => "payable",
693            Self::NonPayable => "nonpayable",
694        }
695    }
696}
697
698/// Visibility ordered from restricted to unrestricted.
699#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
700pub enum Visibility {
701    /// `private`: visible only in the current contract.
702    Private,
703    /// `internal`: visible only in the current contract and contracts deriving from it.
704    Internal,
705    /// `public`: visible internally and externally.
706    Public,
707    /// `external`: visible only externally.
708    External,
709}
710
711impl fmt::Display for Visibility {
712    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713        self.to_str().fmt(f)
714    }
715}
716
717impl Visibility {
718    /// Returns the string representation of the visibility.
719    pub const fn to_str(self) -> &'static str {
720        match self {
721            Self::Private => "private",
722            Self::Internal => "internal",
723            Self::Public => "public",
724            Self::External => "external",
725        }
726    }
727}
728
729/// A state variable or constant definition: `uint256 constant FOO = 42;`.
730///
731/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.stateVariableDeclaration>
732#[derive(Debug)]
733pub struct VariableDefinition<'ast> {
734    pub span: Span,
735    pub ty: Type<'ast>,
736    pub visibility: Option<Visibility>,
737    pub mutability: Option<VarMut>,
738    pub data_location: Option<DataLocation>,
739    pub override_: Option<Override<'ast>>,
740    pub indexed: bool,
741    pub name: Option<Ident>,
742    pub initializer: Option<Box<'ast, Expr<'ast>>>,
743}
744
745/// The mutability of a variable.
746#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
747pub enum VarMut {
748    /// `immutable`
749    Immutable,
750    /// `constant`
751    Constant,
752}
753
754impl fmt::Display for VarMut {
755    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756        f.write_str(self.to_str())
757    }
758}
759
760impl VarMut {
761    /// Returns the string representation of the variable mutability.
762    pub const fn to_str(self) -> &'static str {
763        match self {
764            Self::Immutable => "immutable",
765            Self::Constant => "constant",
766        }
767    }
768
769    /// Returns `true` if the variable is immutable.
770    pub const fn is_immutable(self) -> bool {
771        matches!(self, Self::Immutable)
772    }
773
774    /// Returns `true` if the variable is constant.
775    pub const fn is_constant(self) -> bool {
776        matches!(self, Self::Constant)
777    }
778}
779
780/// A struct definition: `struct Foo { uint256 bar; }`.
781///
782/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.structDefinition>
783#[derive(Debug)]
784pub struct ItemStruct<'ast> {
785    pub name: Ident,
786    pub fields: BoxSlice<'ast, VariableDefinition<'ast>>,
787}
788
789/// An enum definition: `enum Foo { A, B, C }`.
790///
791/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.enumDefinition>
792#[derive(Debug)]
793pub struct ItemEnum<'ast> {
794    pub name: Ident,
795    pub variants: BoxSlice<'ast, Ident>,
796}
797
798/// A user-defined value type definition: `type Foo is uint256;`.
799///
800/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.userDefinedValueTypeDefinition>
801#[derive(Debug)]
802pub struct ItemUdvt<'ast> {
803    pub name: Ident,
804    pub ty: Type<'ast>,
805}
806
807/// An error definition: `error Foo(uint256 a, uint256 b);`.
808///
809/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.errorDefinition>
810#[derive(Debug)]
811pub struct ItemError<'ast> {
812    pub name: Ident,
813    pub parameters: ParameterList<'ast>,
814}
815
816/// An event definition:
817/// `event Transfer(address indexed from, address indexed to, uint256 value);`.
818///
819/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.eventDefinition>
820#[derive(Debug)]
821pub struct ItemEvent<'ast> {
822    pub name: Ident,
823    pub parameters: ParameterList<'ast>,
824    pub anonymous: bool,
825}