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#[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#[derive(Debug)]
38pub struct Item<'ast> {
39 pub docs: DocComments<'ast>,
40 pub span: Span,
41 pub kind: ItemKind<'ast>,
43}
44
45impl Item<'_> {
46 pub fn name(&self) -> Option<Ident> {
48 self.kind.name()
49 }
50
51 pub fn description(&self) -> &'static str {
53 self.kind.description()
54 }
55
56 pub fn is_allowed_in_contract(&self) -> bool {
58 self.kind.is_allowed_in_contract()
59 }
60}
61
62pub enum ItemKind<'ast> {
66 Pragma(PragmaDirective<'ast>),
68
69 Import(ImportDirective<'ast>),
71
72 Using(UsingDirective<'ast>),
74
75 Contract(ItemContract<'ast>),
78
79 Function(ItemFunction<'ast>),
82
83 Variable(VariableDefinition<'ast>),
85
86 Struct(ItemStruct<'ast>),
88
89 Enum(ItemEnum<'ast>),
91
92 Udvt(ItemUdvt<'ast>),
94
95 Error(ItemError<'ast>),
97
98 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 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 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 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#[derive(Debug)]
175pub struct PragmaDirective<'ast> {
176 pub tokens: PragmaTokens<'ast>,
178}
179
180#[derive(Debug)]
182pub enum PragmaTokens<'ast> {
183 Version(Ident, SemverReq<'ast>),
187 Custom(IdentOrStrLit, Option<IdentOrStrLit>),
189 Verbatim(Box<'ast, [Token]>),
191}
192
193impl PragmaTokens<'_> {
194 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#[derive(Clone, Debug)]
225pub enum IdentOrStrLit {
226 Ident(Ident),
228 StrLit(StrLit),
230}
231
232impl IdentOrStrLit {
233 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 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#[derive(Debug)]
254pub struct ImportDirective<'ast> {
255 pub path: StrLit,
259 pub items: ImportItems<'ast>,
260}
261
262impl ImportDirective<'_> {
263 pub fn source_alias(&self) -> Option<Ident> {
265 self.items.source_alias()
266 }
267}
268
269#[derive(Debug)]
271pub enum ImportItems<'ast> {
272 Plain(Option<Ident>),
274 Aliases(Box<'ast, [(Ident, Option<Ident>)]>),
276 Glob(Ident),
278}
279
280impl ImportItems<'_> {
281 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#[derive(Debug)]
295pub struct UsingDirective<'ast> {
296 pub list: UsingList<'ast>,
298 pub ty: Option<Type<'ast>>,
300 pub global: bool,
301}
302
303#[derive(Debug)]
305pub enum UsingList<'ast> {
306 Single(AstPath<'ast>),
308 Multiple(Box<'ast, [(AstPath<'ast>, Option<UserDefinableOperator>)]>),
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
316pub enum UserDefinableOperator {
317 BitAnd,
319 BitNot,
321 BitOr,
323 BitXor,
325 Add,
327 Div,
329 Rem,
331 Mul,
333 Sub,
335 Eq,
337 Ge,
339 Gt,
341 Le,
343 Lt,
345 Ne,
347}
348
349impl UserDefinableOperator {
350 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 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#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
395pub enum ContractKind {
396 Contract,
398 AbstractContract,
400 Interface,
402 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 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#[derive(Debug)]
428pub struct StorageLayoutSpecifier<'ast> {
429 pub span: Span,
430 pub slot: Box<'ast, Expr<'ast>>,
431}
432
433#[derive(Debug)]
438pub struct ItemFunction<'ast> {
439 pub kind: FunctionKind,
441 pub header: FunctionHeader<'ast>,
443 pub body: Option<Block<'ast>>,
445 pub body_span: Span,
447}
448
449impl ItemFunction<'_> {
450 pub fn is_implemented(&self) -> bool {
452 self.body.is_some()
453 }
454}
455
456#[derive(Debug, Default)]
458pub struct FunctionHeader<'ast> {
459 pub span: Span,
461
462 pub name: Option<Ident>,
465
466 pub parameters: ParameterList<'ast>,
468
469 pub visibility: Option<Spanned<Visibility>>,
471
472 pub state_mutability: Option<Spanned<StateMutability>>,
474
475 pub modifiers: Box<'ast, [Modifier<'ast>]>,
477
478 pub virtual_: Option<Span>,
480
481 pub override_: Option<Override<'ast>>,
483
484 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
510pub enum FunctionKind {
511 Constructor,
513 Function,
515 Fallback,
517 Receive,
519 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 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 pub fn allowed_in_global(&self) -> bool {
543 self.is_ordinary()
544 }
545
546 pub fn is_ordinary(&self) -> bool {
548 matches!(self, Self::Function)
549 }
550}
551
552#[derive(Debug)]
557pub struct Modifier<'ast> {
558 pub name: AstPath<'ast>,
559 pub arguments: CallArgs<'ast>,
560}
561
562impl Modifier<'_> {
563 pub fn span(&self) -> Span {
565 self.name.span().to(self.arguments.span)
566 }
567}
568
569#[derive(Debug)]
571pub struct Override<'ast> {
572 pub span: Span,
573 pub paths: Box<'ast, [AstPath<'ast>]>,
574}
575
576#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
578pub enum DataLocation {
579 Storage,
581 Transient,
583 Memory,
585 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 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 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, EnumIs, PartialOrd, Ord)]
617pub enum StateMutability {
618 Pure,
620 View,
622 Payable,
624 #[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 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
649pub enum Visibility {
650 Private,
652 Internal,
654 Public,
656 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 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#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
696pub enum VarMut {
697 Immutable,
699 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 pub const fn to_str(self) -> &'static str {
712 match self {
713 Self::Immutable => "immutable",
714 Self::Constant => "constant",
715 }
716 }
717
718 pub const fn is_immutable(self) -> bool {
720 matches!(self, Self::Immutable)
721 }
722
723 pub const fn is_constant(self) -> bool {
725 matches!(self, Self::Constant)
726 }
727}
728
729#[derive(Debug)]
733pub struct ItemStruct<'ast> {
734 pub name: Ident,
735 pub fields: Box<'ast, [VariableDefinition<'ast>]>,
736}
737
738#[derive(Debug)]
742pub struct ItemEnum<'ast> {
743 pub name: Ident,
744 pub variants: Box<'ast, [Ident]>,
745}
746
747#[derive(Debug)]
751pub struct ItemUdvt<'ast> {
752 pub name: Ident,
753 pub ty: Type<'ast>,
754}
755
756#[derive(Debug)]
760pub struct ItemError<'ast> {
761 pub name: Ident,
762 pub parameters: ParameterList<'ast>,
763}
764
765#[derive(Debug)]
770pub struct ItemEvent<'ast> {
771 pub name: Ident,
772 pub parameters: ParameterList<'ast>,
773 pub anonymous: bool,
774}