Skip to main content

solar_sema/hir/
mod.rs

1//! High-level intermediate representation (HIR).
2
3use crate::{builtins::Builtin, ty::Gcx};
4use derive_more::derive::From;
5use either::Either;
6use rayon::prelude::*;
7use solar_ast as ast;
8use solar_data_structures::{
9    BumpExt,
10    index::{Idx, IndexVec},
11    newtype_index,
12};
13use solar_interface::{Ident, Span, Symbol, diagnostics::ErrorGuaranteed, source_map::SourceFile};
14use std::{cell::Cell, fmt, ops::ControlFlow, sync::Arc};
15use strum::EnumIs;
16
17pub use ast::{
18    BinOp, BinOpKind, ContractKind, DataLocation, ElementaryType, FunctionKind, Lit, NatSpecItem,
19    NatSpecKind, StateMutability, UnOp, UnOpKind, VarMut, Visibility,
20};
21
22mod print;
23pub use print::HirPrinter;
24
25mod visit;
26pub use visit::Visit;
27
28/// HIR arena allocator.
29pub struct Arena {
30    bump: bumpalo::Bump,
31}
32
33impl Arena {
34    /// Creates a new AST arena.
35    pub fn new() -> Self {
36        Self { bump: bumpalo::Bump::new() }
37    }
38
39    /// Returns a reference to the arena's bump allocator.
40    pub fn bump(&self) -> &bumpalo::Bump {
41        &self.bump
42    }
43
44    /// Returns a mutable reference to the arena's bump allocator.
45    pub fn bump_mut(&mut self) -> &mut bumpalo::Bump {
46        &mut self.bump
47    }
48
49    /// Calculates the number of bytes currently allocated in the entire arena.
50    pub fn allocated_bytes(&self) -> usize {
51        self.bump.allocated_bytes()
52    }
53
54    /// Returns the number of bytes currently in use.
55    pub fn used_bytes(&self) -> usize {
56        self.bump.used_bytes()
57    }
58}
59
60impl Default for Arena {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl std::ops::Deref for Arena {
67    type Target = bumpalo::Bump;
68
69    #[inline]
70    fn deref(&self) -> &Self::Target {
71        &self.bump
72    }
73}
74
75/// The high-level intermediate representation (HIR).
76///
77/// This struct contains all the information about the entire program.
78#[derive(Debug)]
79pub struct Hir<'hir> {
80    /// All sources.
81    pub(crate) sources: IndexVec<SourceId, Source<'hir>>,
82    /// All documentation comments.
83    pub(crate) docs: IndexVec<DocId, Doc<'hir>>,
84    /// All contracts.
85    pub(crate) contracts: IndexVec<ContractId, Contract<'hir>>,
86    /// All functions.
87    pub(crate) functions: IndexVec<FunctionId, Function<'hir>>,
88    /// All structs.
89    pub(crate) structs: IndexVec<StructId, Struct<'hir>>,
90    /// All enums.
91    pub(crate) enums: IndexVec<EnumId, Enum<'hir>>,
92    /// All user-defined value types.
93    pub(crate) udvts: IndexVec<UdvtId, Udvt<'hir>>,
94    /// All events.
95    pub(crate) events: IndexVec<EventId, Event<'hir>>,
96    /// All custom errors.
97    pub(crate) errors: IndexVec<ErrorId, Error<'hir>>,
98    /// All constants and variables.
99    pub(crate) variables: IndexVec<VariableId, Variable<'hir>>,
100}
101
102macro_rules! indexvec_methods {
103    ($($singular:ident => $plural:ident, $id:ty => $type:ty;)*) => { paste::paste! {
104        $(
105            #[doc = "Returns the " $singular " associated with the given ID."]
106            #[inline]
107            #[cfg_attr(debug_assertions, track_caller)]
108            pub fn $singular(&self, id: $id) -> &$type {
109                if cfg!(debug_assertions) {
110                    &self.$plural[id]
111                } else {
112                    unsafe { self.$plural.raw.get_unchecked(id.index()) }
113                }
114            }
115
116            #[doc = "Returns an iterator over all of the " $singular " IDs."]
117            #[inline]
118            pub fn [<$singular _ids>](&self) -> impl ExactSizeIterator<Item = $id> + Clone + use<> {
119                // SAFETY: `$plural` is an IndexVec, which guarantees that all indexes are in bounds
120                // of the respective index type.
121                (0..self.$plural.len()).map(|id| unsafe { $id::from_usize_unchecked(id) })
122            }
123
124            #[doc = "Returns a parallel iterator over all of the " $singular " IDs."]
125            #[inline]
126            pub fn [<par_ $singular _ids>](&self) -> impl IndexedParallelIterator<Item = $id> + use<> {
127                // SAFETY: `$plural` is an IndexVec, which guarantees that all indexes are in bounds
128                // of the respective index type.
129                (0..self.$plural.len()).into_par_iter().map(|id| unsafe { $id::from_usize_unchecked(id) })
130            }
131
132            #[doc = "Returns an iterator over all of the " $singular " values."]
133            #[inline]
134            pub fn $plural(&self) -> impl ExactSizeIterator<Item = &$type> + Clone {
135                self.$plural.raw.iter()
136            }
137
138            #[doc = "Returns a parallel iterator over all of the " $singular " values."]
139            #[inline]
140            pub fn [<par_ $plural>](&self) -> impl IndexedParallelIterator<Item = &$type> {
141                self.$plural.raw.par_iter()
142            }
143
144            #[doc = "Returns an iterator over all of the " $singular " IDs and their associated values."]
145            #[inline]
146            pub fn [<$plural _enumerated>](&self) -> impl ExactSizeIterator<Item = ($id, &$type)> + Clone {
147                // SAFETY: `$plural` is an IndexVec, which guarantees that all indexes are in bounds
148                // of the respective index type.
149                self.$plural().enumerate().map(|(i, v)| (unsafe { $id::from_usize_unchecked(i) }, v))
150            }
151
152            #[doc = "Returns an iterator over all of the " $singular " IDs and their associated values."]
153            #[inline]
154            pub fn [<par_ $plural _enumerated>](&self) -> impl IndexedParallelIterator<Item = ($id, &$type)> {
155                // SAFETY: `$plural` is an IndexVec, which guarantees that all indexes are in bounds
156                // of the respective index type.
157                self.[<par_ $plural>]().enumerate().map(|(i, v)| (unsafe { $id::from_usize_unchecked(i) }, v))
158            }
159        )*
160
161        pub(crate) fn shrink_to_fit(&mut self) {
162            $(
163                self.$plural.shrink_to_fit();
164            )*
165        }
166    }};
167}
168
169impl<'hir> Hir<'hir> {
170    pub(crate) fn new() -> Self {
171        let mut docs = IndexVec::new();
172        let empty_doc_id = docs.push(Doc {
173            source: SourceId::MAX,
174            item: ItemId::Contract(ContractId::MAX),
175            ast_comments: ast::DocComments::default(),
176        });
177        debug_assert_eq!(empty_doc_id, DocId::EMPTY);
178
179        Self {
180            sources: IndexVec::new(),
181            docs,
182            contracts: IndexVec::new(),
183            functions: IndexVec::new(),
184            structs: IndexVec::new(),
185            enums: IndexVec::new(),
186            udvts: IndexVec::new(),
187            events: IndexVec::new(),
188            errors: IndexVec::new(),
189            variables: IndexVec::new(),
190        }
191    }
192
193    indexvec_methods! {
194        source => sources, SourceId => Source<'hir>;
195        doc => docs, DocId => Doc<'hir>;
196        contract => contracts, ContractId => Contract<'hir>;
197        function => functions, FunctionId => Function<'hir>;
198        strukt => structs, StructId => Struct<'hir>;
199        enumm => enums, EnumId => Enum<'hir>;
200        udvt => udvts, UdvtId => Udvt<'hir>;
201        event => events, EventId => Event<'hir>;
202        error => errors, ErrorId => Error<'hir>;
203        variable => variables, VariableId => Variable<'hir>;
204    }
205
206    /// Returns the item associated with the given ID.
207    #[inline]
208    pub fn item(&self, id: impl Into<ItemId>) -> Item<'_, 'hir> {
209        match id.into() {
210            ItemId::Contract(id) => Item::Contract(self.contract(id)),
211            ItemId::Function(id) => Item::Function(self.function(id)),
212            ItemId::Variable(id) => Item::Variable(self.variable(id)),
213            ItemId::Struct(id) => Item::Struct(self.strukt(id)),
214            ItemId::Enum(id) => Item::Enum(self.enumm(id)),
215            ItemId::Udvt(id) => Item::Udvt(self.udvt(id)),
216            ItemId::Error(id) => Item::Error(self.error(id)),
217            ItemId::Event(id) => Item::Event(self.event(id)),
218        }
219    }
220
221    /// Returns an iterator over all item IDs.
222    pub fn item_ids(&self) -> impl Iterator<Item = ItemId> + Clone {
223        self.item_ids_vec().into_iter()
224    }
225
226    /// Returns a parallel iterator over all item IDs.
227    pub fn par_item_ids(&self) -> impl ParallelIterator<Item = ItemId> {
228        self.item_ids_vec().into_par_iter()
229    }
230
231    fn item_ids_vec(&self) -> Vec<ItemId> {
232        // NOTE: This is essentially an unrolled `.chain().chain() ... .collect()` since it's not
233        // very efficient.
234        #[rustfmt::skip]
235        let len =
236              self.contracts.len()
237            + self.functions.len()
238            + self.variables.len()
239            + self.structs.len()
240            + self.enums.len()
241            + self.udvts.len()
242            + self.errors.len()
243            + self.events.len();
244        let mut v = Vec::<ItemId>::with_capacity(len);
245        let mut items = v.spare_capacity_mut().iter_mut();
246        macro_rules! extend_unchecked {
247            ($iter:expr) => {
248                for item in $iter {
249                    unsafe { items.next().unwrap_unchecked().write(item) };
250                }
251            };
252        }
253        extend_unchecked!(self.contract_ids().map(ItemId::from));
254        extend_unchecked!(self.function_ids().map(ItemId::from));
255        extend_unchecked!(self.variable_ids().map(ItemId::from));
256        extend_unchecked!(self.strukt_ids().map(ItemId::from));
257        extend_unchecked!(self.enumm_ids().map(ItemId::from));
258        extend_unchecked!(self.udvt_ids().map(ItemId::from));
259        extend_unchecked!(self.error_ids().map(ItemId::from));
260        extend_unchecked!(self.event_ids().map(ItemId::from));
261
262        debug_assert!(items.next().is_none());
263        unsafe { v.set_len(len) };
264        debug_assert_eq!(v.len(), len);
265        debug_assert_eq!(v.capacity(), len);
266
267        v
268    }
269
270    /// Returns an iterator over all item IDs in a contract, including inheritance.
271    pub fn contract_item_ids(
272        &self,
273        id: ContractId,
274    ) -> impl Iterator<Item = ItemId> + Clone + use<'_, 'hir> {
275        self.contract(id)
276            .linearized_bases
277            .iter()
278            .copied()
279            .flat_map(|base| self.contract(base).items.iter().copied())
280    }
281
282    /// Returns an iterator over all items in a contract, including inheritance.
283    pub fn contract_items(&self, id: ContractId) -> impl Iterator<Item = Item<'_, 'hir>> + Clone {
284        self.contract_item_ids(id).map(move |id| self.item(id))
285    }
286
287    /// Creates a builder for constructing HIR nodes.
288    pub fn builder<'id>(
289        arena: &'hir bumpalo::Bump,
290        next_id: &'id IdCounter,
291    ) -> HirBuilder<'hir, 'id> {
292        HirBuilder::new(arena, next_id)
293    }
294}
295
296/// A counter for generating unique IDs.
297pub struct IdCounter {
298    counter: Cell<u32>,
299}
300
301impl Default for IdCounter {
302    #[inline]
303    fn default() -> Self {
304        Self::new()
305    }
306}
307
308impl IdCounter {
309    /// Creates a new ID counter.
310    #[inline]
311    pub fn new() -> Self {
312        Self { counter: Cell::new(0) }
313    }
314
315    /// Generates the next ID.
316    #[inline]
317    pub fn next<I: Idx>(&self) -> I {
318        I::from_usize(self.next_usize())
319    }
320
321    /// Generates the next ID as a usize.
322    #[inline]
323    pub fn next_usize(&self) -> usize {
324        let x = self.counter.get();
325        self.counter.set(x + 1);
326        x as usize
327    }
328}
329
330/// A builder for constructing HIR nodes.
331pub struct HirBuilder<'hir, 'id> {
332    arena: &'hir bumpalo::Bump,
333    next_id: &'id IdCounter,
334}
335
336impl<'hir, 'id> HirBuilder<'hir, 'id> {
337    /// Creates a new HIR builder.
338    pub fn new(arena: &'hir bumpalo::Bump, next_id: &'id IdCounter) -> Self {
339        Self { arena, next_id }
340    }
341
342    /// Generates the next expression ID.
343    pub fn next_expr_id(&self) -> ExprId {
344        self.next_id.next()
345    }
346
347    /// Creates a HIR expression with ID, kind and span.
348    pub fn expr(&self, id: ExprId, kind: ExprKind<'hir>, span: Span) -> &'hir Expr<'hir> {
349        self.arena.alloc(Expr { id, kind, span })
350    }
351
352    /// Creates a HIR expression with the given kind (as requested in GitHub issue).
353    pub fn expr_kind(&self, kind: ExprKind<'hir>) -> Expr<'hir> {
354        Expr { id: self.next_expr_id(), kind, span: Span::DUMMY }
355    }
356
357    /// Creates an allocated HIR expression.
358    pub fn expr_alloc(&self, id: ExprId, kind: ExprKind<'hir>, span: Span) -> &'hir Expr<'hir> {
359        self.arena.alloc(Expr { id, kind, span })
360    }
361
362    /// Creates an allocated HIR statement.
363    pub fn stmt_alloc(&self, kind: StmtKind<'hir>, span: Span) -> &'hir Stmt<'hir> {
364        self.arena.alloc(Stmt { kind, span })
365    }
366
367    /// Creates a break statement.
368    pub fn break_stmt(&self, span: Span) -> &'hir Stmt<'hir> {
369        self.stmt_alloc(StmtKind::Break, span)
370    }
371
372    /// Creates a continue statement.
373    pub fn continue_stmt(&self, span: Span) -> &'hir Stmt<'hir> {
374        self.stmt_alloc(StmtKind::Continue, span)
375    }
376
377    /// Creates a return statement.
378    pub fn return_stmt(&self, expr: Option<&'hir Expr<'hir>>, span: Span) -> &'hir Stmt<'hir> {
379        self.stmt_alloc(StmtKind::Return(expr), span)
380    }
381
382    /// Creates a binary expression kind.
383    pub fn binary_expr(
384        &self,
385        left: &'hir Expr<'hir>,
386        op: BinOp,
387        right: &'hir Expr<'hir>,
388    ) -> ExprKind<'hir> {
389        ExprKind::Binary(left, op, right)
390    }
391
392    /// Creates a literal expression kind.
393    pub fn lit_expr(&self, lit: &'hir Lit<'hir>) -> ExprKind<'hir> {
394        ExprKind::Lit(lit)
395    }
396
397    /// Creates an owned HIR expression with the given ID, kind and span.
398    pub fn expr_owned(&self, id: ExprId, kind: ExprKind<'hir>, span: Span) -> Expr<'hir> {
399        Expr { id, kind, span }
400    }
401
402    /// Creates an owned HIR expression with automatically generated ID.
403    pub fn expr_auto(&self, kind: ExprKind<'hir>, span: Span) -> Expr<'hir> {
404        Expr { id: self.next_expr_id(), kind, span }
405    }
406
407    /// Creates a HIR statement with the given kind and span.
408    pub fn stmt(&self, kind: StmtKind<'hir>, span: Span) -> Stmt<'hir> {
409        Stmt { kind, span }
410    }
411
412    /// Creates a HIR block with the given statements and span.
413    pub fn block(&self, stmts: &'hir [Stmt<'hir>], span: Span) -> Block<'hir> {
414        Block { stmts, span }
415    }
416}
417
418newtype_index! {
419    /// A [`Source`] ID.
420    pub struct SourceId;
421
422    /// A [`Doc`] ID.
423    ///
424    /// Use [`Gcx::natspec_doc_comments`] to access validated and resolved NatSpec items.
425    pub struct DocId;
426
427    /// A [`Contract`] ID.
428    pub struct ContractId;
429
430    /// A [`Function`] ID.
431    pub struct FunctionId;
432
433    /// A [`Struct`] ID.
434    pub struct StructId;
435
436    /// An [`Enum`] ID.
437    pub struct EnumId;
438
439    /// An [`Udvt`] ID.
440    pub struct UdvtId;
441
442    /// An [`Event`] ID.
443    pub struct EventId;
444
445    /// An [`Error`] ID.
446    pub struct ErrorId;
447
448    /// A [`Variable`] ID.
449    pub struct VariableId;
450
451    /// An [`Expr`] ID.
452    pub struct ExprId;
453}
454
455impl DocId {
456    /// The empty documentation id.
457    ///
458    /// This is reserved as index 0 and represents items with no documentation.
459    pub const EMPTY: Self = Self::new(0);
460
461    /// Whether a documentation id is empty or not.
462    pub fn is_empty(&self) -> bool {
463        self == &Self::EMPTY
464    }
465}
466
467/// A source file.
468pub struct Source<'hir> {
469    pub file: Arc<SourceFile>,
470    /// The individual imports with their resolved source IDs.
471    ///
472    /// Note that the source IDs may not be unique, as multiple imports may resolve to the same
473    /// source.
474    pub imports: &'hir [(ast::ItemId, SourceId)],
475    /// The source items.
476    pub items: &'hir [ItemId],
477    /// The source-level `using for` directives.
478    pub usings: &'hir [UsingDirective<'hir>],
479    /// The source docs.
480    pub docs: &'hir [DocId],
481}
482
483impl fmt::Debug for Source<'_> {
484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485        f.debug_struct("Source")
486            .field("file", &self.file.name)
487            .field("imports", &self.imports)
488            .field("items", &self.items)
489            .finish()
490    }
491}
492
493/// The documentation of an item.
494///
495/// Use [`Gcx::natspec_doc_comments`] with the corresponding [`DocId`] to access validated and
496/// resolved NatSpec items.
497#[derive(Debug)]
498pub struct Doc<'hir> {
499    /// The source this documentation is defined in.
500    pub source: SourceId,
501    /// The item this documentation is defined in.
502    pub item: ItemId,
503    /// Reference to the raw AST documentation comments.
504    ///
505    /// Use [`Gcx::natspec_doc_comments`] for the validated and resolved NatSpec view.
506    pub(crate) ast_comments: ast::DocComments<'hir>,
507}
508
509#[derive(Clone, Copy, Debug, EnumIs)]
510pub enum Item<'a, 'hir> {
511    Contract(&'a Contract<'hir>),
512    Function(&'a Function<'hir>),
513    Struct(&'a Struct<'hir>),
514    Enum(&'a Enum<'hir>),
515    Udvt(&'a Udvt<'hir>),
516    Error(&'a Error<'hir>),
517    Event(&'a Event<'hir>),
518    Variable(&'a Variable<'hir>),
519}
520
521impl<'hir> Item<'_, 'hir> {
522    /// Returns the name of the item.
523    #[inline]
524    pub fn name(self) -> Option<Ident> {
525        match self {
526            Item::Contract(c) => Some(c.name),
527            Item::Function(f) => f.name,
528            Item::Struct(s) => Some(s.name),
529            Item::Enum(e) => Some(e.name),
530            Item::Udvt(u) => Some(u.name),
531            Item::Error(e) => Some(e.name),
532            Item::Event(e) => Some(e.name),
533            Item::Variable(v) => v.name,
534        }
535    }
536
537    /// Returns the description of the item.
538    #[inline]
539    pub fn description(self) -> &'static str {
540        match self {
541            Item::Contract(c) => c.description(),
542            Item::Function(f) => f.description(),
543            Item::Struct(_) => "struct",
544            Item::Enum(_) => "enum",
545            Item::Udvt(_) => "UDVT",
546            Item::Error(_) => "error",
547            Item::Event(_) => "event",
548            Item::Variable(_) => "variable",
549        }
550    }
551
552    /// Returns the span of the item.
553    #[inline]
554    pub fn span(self) -> Span {
555        match self {
556            Item::Contract(c) => c.span,
557            Item::Function(f) => f.span,
558            Item::Struct(s) => s.span,
559            Item::Enum(e) => e.span,
560            Item::Udvt(u) => u.span,
561            Item::Error(e) => e.span,
562            Item::Event(e) => e.span,
563            Item::Variable(v) => v.span,
564        }
565    }
566
567    /// Returns the contract ID if this item is part of a contract.
568    #[inline]
569    pub fn contract(self) -> Option<ContractId> {
570        match self {
571            Item::Contract(_) => None,
572            Item::Function(f) => f.contract,
573            Item::Struct(s) => s.contract,
574            Item::Enum(e) => e.contract,
575            Item::Udvt(u) => u.contract,
576            Item::Error(e) => e.contract,
577            Item::Event(e) => e.contract,
578            Item::Variable(v) => v.contract,
579        }
580    }
581
582    /// Returns the parent item that owns this item's lexical scope, if any.
583    #[inline]
584    pub fn parent(self) -> Option<ItemId> {
585        match self {
586            Item::Variable(v) => v.parent.or_else(|| v.contract.map(ItemId::Contract)),
587            _ => self.contract().map(ItemId::Contract),
588        }
589    }
590
591    /// Returns the source ID where this item is defined.
592    #[inline]
593    pub fn source(self) -> SourceId {
594        match self {
595            Item::Contract(c) => c.source,
596            Item::Function(f) => f.source,
597            Item::Struct(s) => s.source,
598            Item::Enum(e) => e.source,
599            Item::Udvt(u) => u.source,
600            Item::Error(e) => e.source,
601            Item::Event(e) => e.source,
602            Item::Variable(v) => v.source,
603        }
604    }
605
606    /// Returns the documentation comments associated with this item.
607    #[inline]
608    pub fn doc(self) -> DocId {
609        match self {
610            Item::Contract(c) => c.doc,
611            Item::Function(f) => f.doc,
612            Item::Struct(s) => s.doc,
613            Item::Enum(e) => e.doc,
614            Item::Udvt(u) => u.doc,
615            Item::Error(e) => e.doc,
616            Item::Event(e) => e.doc,
617            Item::Variable(v) => v.doc,
618        }
619    }
620
621    /// Returns the parameters of the item.
622    #[inline]
623    pub fn parameters(self) -> Option<&'hir [VariableId]> {
624        Some(match self {
625            Item::Struct(s) => s.fields,
626            Item::Function(f) => f.parameters,
627            Item::Event(e) => e.parameters,
628            Item::Error(e) => e.parameters,
629            _ => return None,
630        })
631    }
632
633    /// Returns `true` if the item is visible in derived contracts.
634    #[inline]
635    pub fn is_visible_in_derived_contracts(self) -> bool {
636        self.is_visible_in_contract() && self.visibility() >= Visibility::Internal
637    }
638
639    /// Returns `true` if the item is visible in the contract.
640    #[inline]
641    pub fn is_visible_in_contract(self) -> bool {
642        (if let Item::Function(f) = self {
643            matches!(f.kind, FunctionKind::Function | FunctionKind::Modifier)
644        } else {
645            true
646        }) && self.visibility() != Visibility::External
647    }
648
649    /// Returns `true` if the item is visible as a library member.
650    #[inline]
651    pub fn is_visible_as_library_member(self) -> bool {
652        self.visibility() >= Visibility::Internal
653    }
654
655    /// Returns `true` if the item is visible through contract type access.
656    #[inline]
657    pub fn is_visible_via_contract_type_access(self) -> bool {
658        match self {
659            Item::Function(f) => f.is_ordinary() && f.visibility >= Visibility::Public,
660            Item::Struct(_) | Item::Enum(_) | Item::Udvt(_) | Item::Error(_) | Item::Event(_) => {
661                true
662            }
663            Item::Contract(_) | Item::Variable(_) => false,
664        }
665    }
666
667    /// Returns `true` if the item is public or external.
668    #[inline]
669    pub fn is_public(&self) -> bool {
670        self.visibility() >= Visibility::Public
671    }
672
673    /// Returns the visibility of the item.
674    #[inline]
675    pub fn visibility(self) -> Visibility {
676        match self {
677            Item::Variable(v) => v.visibility.unwrap_or(Visibility::Internal),
678            Item::Contract(_)
679            | Item::Function(_)
680            | Item::Struct(_)
681            | Item::Enum(_)
682            | Item::Udvt(_)
683            | Item::Error(_)
684            | Item::Event(_) => Visibility::Public,
685        }
686    }
687}
688
689#[derive(Clone, Copy, PartialEq, Eq, Hash, From, EnumIs)]
690pub enum ItemId {
691    Contract(ContractId),
692    Function(FunctionId),
693    Variable(VariableId),
694    Struct(StructId),
695    Enum(EnumId),
696    Udvt(UdvtId),
697    Error(ErrorId),
698    Event(EventId),
699}
700
701impl fmt::Debug for ItemId {
702    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703        f.write_str("ItemId::")?;
704        match self {
705            Self::Contract(id) => id.fmt(f),
706            Self::Function(id) => id.fmt(f),
707            Self::Variable(id) => id.fmt(f),
708            Self::Struct(id) => id.fmt(f),
709            Self::Enum(id) => id.fmt(f),
710            Self::Udvt(id) => id.fmt(f),
711            Self::Error(id) => id.fmt(f),
712            Self::Event(id) => id.fmt(f),
713        }
714    }
715}
716
717impl ItemId {
718    /// Returns the description of the item.
719    pub fn description(&self) -> &'static str {
720        match self {
721            Self::Contract(_) => "contract",
722            Self::Function(_) => "function",
723            Self::Variable(_) => "variable",
724            Self::Struct(_) => "struct",
725            Self::Enum(_) => "enum",
726            Self::Udvt(_) => "UDVT",
727            Self::Error(_) => "error",
728            Self::Event(_) => "event",
729        }
730    }
731
732    /// Returns `true` if the **item kinds** match.
733    #[inline]
734    pub fn matches(&self, other: &Self) -> bool {
735        std::mem::discriminant(self) == std::mem::discriminant(other)
736    }
737
738    /// Returns the parent item that owns this item's lexical scope, if any.
739    #[inline]
740    pub fn parent(self, hir: &Hir<'_>) -> Option<Self> {
741        hir.item(self).parent()
742    }
743
744    /// Returns the contract ID if this is a contract.
745    pub fn as_contract(&self) -> Option<ContractId> {
746        if let Self::Contract(v) = *self { Some(v) } else { None }
747    }
748
749    /// Returns the function ID if this is a function.
750    pub fn as_function(&self) -> Option<FunctionId> {
751        if let Self::Function(v) = *self { Some(v) } else { None }
752    }
753
754    /// Returns the struct ID if this is a struct.
755    pub fn as_struct(&self) -> Option<StructId> {
756        if let Self::Struct(v) = *self { Some(v) } else { None }
757    }
758
759    /// Returns the variable ID if this is a variable.
760    pub fn as_variable(&self) -> Option<VariableId> {
761        if let Self::Variable(v) = *self { Some(v) } else { None }
762    }
763}
764
765/// A contract, interface, or library.
766#[derive(Debug)]
767pub struct Contract<'hir> {
768    /// The source this contract is defined in.
769    pub source: SourceId,
770    /// The documentation of this contract.
771    pub doc: DocId,
772    /// The contract span.
773    pub span: Span,
774    /// The contract name.
775    pub name: Ident,
776    /// The contract kind.
777    pub kind: ContractKind,
778    /// The storage layout base slot expression, if specified.
779    pub layout: Option<&'hir Expr<'hir>>,
780    /// The contract bases, as declared in the source code.
781    pub bases: &'hir [ContractId],
782    /// The base arguments, as declared in the source code.
783    pub bases_args: &'hir [Modifier<'hir>],
784    /// The linearized contract bases.
785    ///
786    /// The first element is always the contract itself, followed by its bases in order of
787    /// inheritance. The bases may not be present if the inheritance linearization failed. See
788    /// [`Contract::linearization_failed`].
789    pub linearized_bases: &'hir [ContractId],
790    /// The constructor base arguments (if any).
791    ///
792    /// The index maps to the position in `linearized_bases[1..]`.
793    ///
794    /// The reference points to either `bases_args` in the original contract, or `modifiers` in the
795    /// constructor.
796    pub linearized_bases_args: &'hir [Option<&'hir Modifier<'hir>>],
797    /// The resolved constructor function.
798    pub ctor: Option<FunctionId>,
799    /// The resolved `fallback` function.
800    pub fallback: Option<FunctionId>,
801    /// The resolved `receive` function.
802    pub receive: Option<FunctionId>,
803    /// The contract items.
804    ///
805    /// Note that this only includes items defined in the contract itself, not inherited items.
806    /// For getting all items, use [`Hir::contract_items`].
807    pub items: &'hir [ItemId],
808    /// The contract-level `using for` directives.
809    pub usings: &'hir [UsingDirective<'hir>],
810}
811
812impl Contract<'_> {
813    /// Returns `true` if the inheritance linearization failed.
814    pub fn linearization_failed(&self) -> bool {
815        self.linearized_bases.is_empty()
816            || (!self.bases.is_empty() && self.linearized_bases.len() == 1)
817    }
818
819    /// Returns an iterator over functions declared in the contract.
820    ///
821    /// Note that this does not include the constructor and fallback functions, as they are stored
822    /// separately. Use [`Contract::all_functions`] to include them.
823    pub fn functions(&self) -> impl Iterator<Item = FunctionId> + Clone + use<'_> {
824        self.items.iter().filter_map(ItemId::as_function)
825    }
826
827    /// Returns an iterator over all functions declared in the contract.
828    pub fn all_functions(&self) -> impl Iterator<Item = FunctionId> + Clone + use<'_> {
829        self.functions().chain(self.ctor).chain(self.fallback).chain(self.receive)
830    }
831
832    /// Returns an iterator over all variables declared in the contract.
833    pub fn variables(&self) -> impl Iterator<Item = VariableId> + Clone + use<'_> {
834        self.items.iter().filter_map(ItemId::as_variable)
835    }
836
837    /// Returns `true` if the contract can be deployed.
838    pub fn can_be_deployed(&self) -> bool {
839        matches!(self.kind, ContractKind::Contract | ContractKind::Library)
840    }
841
842    /// Returns `true` if this is an abstract contract.
843    pub fn is_abstract(&self) -> bool {
844        self.kind.is_abstract_contract()
845    }
846
847    /// Returns the description of the contract.
848    pub fn description(&self) -> &'static str {
849        self.kind.to_str()
850    }
851}
852
853/// Returns `true` if the contract can receive ether.
854///
855/// A contract can receive ether if it has:
856/// - A `receive()` function, OR
857/// - A `fallback()` function with `payable` state mutability
858pub fn can_receive_ether(contract: &Contract<'_>, gcx: Gcx<'_>) -> bool {
859    // Check if contract has receive function
860    if contract.receive.is_some() {
861        return true;
862    }
863
864    // Check if contract has payable fallback function
865    if let Some(fallback_id) = contract.fallback {
866        let fallback = gcx.hir.function(fallback_id);
867        if fallback.state_mutability == StateMutability::Payable {
868            return true;
869        }
870    }
871
872    false
873}
874
875/// A modifier or base class call.
876#[derive(Clone, Copy, Debug)]
877pub struct Modifier<'hir> {
878    /// The span of the modifier or base class call.
879    pub span: Span,
880    /// The modifier or base class ID.
881    pub id: ItemId,
882    /// The arguments to the modifier or base class call.
883    pub args: CallArgs<'hir>,
884}
885
886/// A resolved `using for` directive.
887#[derive(Debug)]
888pub struct UsingDirective<'hir> {
889    /// The directive span.
890    pub span: Span,
891    /// The source this directive is defined in.
892    pub source: SourceId,
893    /// The contract this directive is defined in, if any.
894    pub contract: Option<ContractId>,
895    /// The type this directive applies to. `None` means `*`.
896    pub ty: Option<Type<'hir>>,
897    /// Whether this is a global directive.
898    pub global: bool,
899    /// The attached library/functions.
900    pub entries: &'hir [UsingEntry<'hir>],
901}
902
903/// A resolved entry in a `using for` directive.
904#[derive(Debug)]
905pub struct UsingEntry<'hir> {
906    /// The path span.
907    pub span: Span,
908    /// The member name introduced by a braced function entry.
909    pub name: Option<Symbol>,
910    /// The attached item.
911    pub kind: UsingEntryKind<'hir>,
912    /// The operator this entry defines, if any.
913    pub operator: Option<ast::UserDefinableOperator>,
914}
915
916/// A resolved `using for` entry kind.
917#[derive(Debug)]
918pub enum UsingEntryKind<'hir> {
919    /// `using L for T`.
920    Library(ContractId),
921    /// `using { f } for T`.
922    Functions(&'hir [FunctionId]),
923    /// An erroneous entry.
924    Err(ErrorGuaranteed),
925}
926
927/// A function.
928#[derive(Debug)]
929pub struct Function<'hir> {
930    /// The source this function is defined in.
931    pub source: SourceId,
932    /// The documentation of this function.
933    pub doc: DocId,
934    /// The contract this function is defined in, if any.
935    pub contract: Option<ContractId>,
936    /// The function span.
937    pub span: Span,
938    /// The function name.
939    /// Only `None` if this is a constructor, fallback, or receive function.
940    pub name: Option<Ident>,
941    /// The function kind.
942    pub kind: FunctionKind,
943    /// Whether this function was lowered from a Yul function definition.
944    pub is_yul: bool,
945    /// The visibility of the function.
946    pub visibility: Visibility,
947    /// The state mutability of the function.
948    pub state_mutability: StateMutability,
949    /// Modifiers, or base classes if this is a constructor.
950    pub modifiers: &'hir [Modifier<'hir>],
951    /// Whether this function is marked with the `virtual` keyword.
952    pub marked_virtual: bool,
953    /// Whether this function is marked with the `virtual` keyword or is defined in an interface.
954    pub virtual_: bool,
955    /// Whether this function is marked with the `override` keyword.
956    pub override_: bool,
957    pub overrides: &'hir [ContractId],
958    /// The function parameters.
959    pub parameters: &'hir [VariableId],
960    /// The function returns.
961    pub returns: &'hir [VariableId],
962    /// The function body.
963    pub body: Option<Block<'hir>>,
964    /// The function body span.
965    pub body_span: Span,
966    /// The variable this function is a getter of, if any.
967    pub gettee: Option<VariableId>,
968}
969
970impl Function<'_> {
971    /// Returns the span of the `kind` keyword.
972    pub fn keyword_span(&self) -> Span {
973        self.span.with_hi(self.span.lo() + self.kind.to_str().len() as u32)
974    }
975
976    /// Returns `true` if this is a free function, meaning it is not part of a contract.
977    pub fn is_free(&self) -> bool {
978        self.contract.is_none()
979    }
980
981    pub fn is_ordinary(&self) -> bool {
982        self.kind.is_ordinary()
983    }
984
985    /// Returns `true` if this is a getter function of a variable.
986    pub fn is_getter(&self) -> bool {
987        self.gettee.is_some()
988    }
989
990    pub fn is_part_of_external_interface(&self) -> bool {
991        self.is_ordinary() && self.visibility >= Visibility::Public
992    }
993
994    /// Returns `true` if this is a receive or fallback function
995    pub fn is_special(&self) -> bool {
996        // https://docs.soliditylang.org/en/latest/contracts.html#special-functions
997        matches!(self.kind, FunctionKind::Receive | FunctionKind::Fallback)
998    }
999
1000    /// Returns `true` if this is a constructor
1001    pub fn is_constructor(&self) -> bool {
1002        matches!(self.kind, FunctionKind::Constructor)
1003    }
1004
1005    /// Returns `true` if this function mutates state
1006    pub fn mutates_state(&self) -> bool {
1007        self.state_mutability >= StateMutability::Payable
1008    }
1009
1010    /// Returns an iterator over all variables in the function.
1011    pub fn variables(&self) -> impl DoubleEndedIterator<Item = VariableId> + Clone + use<'_> {
1012        self.parameters.iter().copied().chain(self.returns.iter().copied())
1013    }
1014
1015    /// Returns the description of the function.
1016    pub fn description(&self) -> &'static str {
1017        if self.is_getter() { "getter function" } else { self.kind.to_str() }
1018    }
1019}
1020
1021/// A struct.
1022#[derive(Debug)]
1023pub struct Struct<'hir> {
1024    /// The source this struct is defined in.
1025    pub source: SourceId,
1026    /// The documentation of this struct.
1027    pub doc: DocId,
1028    /// The contract this struct is defined in, if any.
1029    pub contract: Option<ContractId>,
1030    /// The struct span.
1031    pub span: Span,
1032    /// The struct name.
1033    pub name: Ident,
1034    pub fields: &'hir [VariableId],
1035}
1036
1037/// An enum.
1038#[derive(Debug)]
1039pub struct Enum<'hir> {
1040    /// The source this enum is defined in.
1041    pub source: SourceId,
1042    /// The documentation of this enum.
1043    pub doc: DocId,
1044    /// The contract this enum is defined in, if any.
1045    pub contract: Option<ContractId>,
1046    /// The enum span.
1047    pub span: Span,
1048    /// The enum name.
1049    pub name: Ident,
1050    /// The enum variants.
1051    pub variants: &'hir [Ident],
1052}
1053
1054/// A user-defined value type.
1055#[derive(Debug)]
1056pub struct Udvt<'hir> {
1057    /// The source this UDVT is defined in.
1058    pub source: SourceId,
1059    /// The documentation of this UDVT.
1060    pub doc: DocId,
1061    /// The contract this UDVT is defined in, if any.
1062    pub contract: Option<ContractId>,
1063    /// The UDVT span.
1064    pub span: Span,
1065    /// The UDVT name.
1066    pub name: Ident,
1067    /// The UDVT type.
1068    pub ty: Type<'hir>,
1069}
1070
1071/// An event.
1072#[derive(Debug)]
1073pub struct Event<'hir> {
1074    /// The source this event is defined in.
1075    pub source: SourceId,
1076    /// The documentation of this event.
1077    pub doc: DocId,
1078    /// The contract this event is defined in, if any.
1079    pub contract: Option<ContractId>,
1080    /// The event span.
1081    pub span: Span,
1082    /// The event name.
1083    pub name: Ident,
1084    /// Whether this event is anonymous.
1085    pub anonymous: bool,
1086    pub parameters: &'hir [VariableId],
1087}
1088
1089/// A custom error.
1090#[derive(Debug)]
1091pub struct Error<'hir> {
1092    /// The source this error is defined in.
1093    pub source: SourceId,
1094    /// The documentation of this error.
1095    pub doc: DocId,
1096    /// The contract this error is defined in, if any.
1097    pub contract: Option<ContractId>,
1098    /// The error span.
1099    pub span: Span,
1100    /// The error name.
1101    pub name: Ident,
1102    pub parameters: &'hir [VariableId],
1103}
1104
1105/// A constant or variable declaration.
1106#[derive(Debug)]
1107pub struct Variable<'hir> {
1108    /// The source this variable is defined in.
1109    pub source: SourceId,
1110    /// The documentation of this variable.
1111    pub doc: DocId,
1112    /// The contract this variable is defined in, if any.
1113    pub contract: Option<ContractId>,
1114    /// The parent item containing this variable.
1115    ///
1116    /// - Struct fields → the struct
1117    /// - Event/Error parameters → the event/error
1118    /// - Function parameters/returns/locals → the function
1119    /// - State/Global variables → `None`, as they are items themselves
1120    pub parent: Option<ItemId>,
1121    /// The variable's span.
1122    pub span: Span,
1123    /// The kind of variable.
1124    pub kind: VarKind,
1125    /// The variable's type.
1126    pub ty: Type<'hir>,
1127    /// The variable's name.
1128    pub name: Option<Ident>,
1129    /// The visibility of the variable.
1130    pub visibility: Option<Visibility>,
1131    pub mutability: Option<VarMut>,
1132    pub data_location: Option<DataLocation>,
1133    pub override_: bool,
1134    pub overrides: &'hir [ContractId],
1135    pub indexed: bool,
1136    pub initializer: Option<&'hir Expr<'hir>>,
1137    /// The compiler-generated getter function, if any.
1138    pub getter: Option<FunctionId>,
1139}
1140
1141impl<'hir> Variable<'hir> {
1142    /// Creates a new variable.
1143    pub fn new(
1144        source: SourceId,
1145        doc: DocId,
1146        ty: Type<'hir>,
1147        name: Option<Ident>,
1148        kind: VarKind,
1149    ) -> Self {
1150        Self {
1151            source,
1152            doc,
1153            contract: None,
1154            parent: None,
1155            span: Span::DUMMY,
1156            kind,
1157            ty,
1158            name,
1159            visibility: None,
1160            mutability: None,
1161            data_location: None,
1162            override_: false,
1163            overrides: &[],
1164            indexed: false,
1165            initializer: None,
1166            getter: None,
1167        }
1168    }
1169
1170    /// Creates a new variable statement.
1171    pub fn new_stmt(
1172        source: SourceId,
1173        docs: DocId,
1174        contract: ContractId,
1175        function: FunctionId,
1176        ty: Type<'hir>,
1177        name: Ident,
1178    ) -> Self {
1179        Self {
1180            contract: Some(contract),
1181            parent: Some(ItemId::Function(function)),
1182            ..Self::new(source, docs, ty, Some(name), VarKind::Statement)
1183        }
1184    }
1185
1186    /// Returns the description of the variable.
1187    pub fn description(&self) -> &'static str {
1188        self.kind.to_str()
1189    }
1190
1191    /// Returns `true` if the variable is [`constant`](VarMut::Constant).
1192    pub fn is_constant(&self) -> bool {
1193        self.mutability == Some(VarMut::Constant)
1194    }
1195
1196    /// Returns `true` if the variable is [`immutable`](VarMut::Immutable).
1197    pub fn is_immutable(&self) -> bool {
1198        self.mutability == Some(VarMut::Immutable)
1199    }
1200
1201    pub fn is_l_value(&self) -> bool {
1202        !self.is_constant()
1203    }
1204
1205    pub fn is_struct_member(&self) -> bool {
1206        matches!(self.kind, VarKind::Struct)
1207    }
1208
1209    pub fn is_event_or_error_parameter(&self) -> bool {
1210        matches!(self.kind, VarKind::Event | VarKind::Error)
1211    }
1212
1213    pub fn is_local_variable(&self) -> bool {
1214        matches!(
1215            self.kind,
1216            VarKind::FunctionTyParam
1217                | VarKind::FunctionTyReturn
1218                | VarKind::Event
1219                | VarKind::Error
1220                | VarKind::FunctionParam
1221                | VarKind::FunctionReturn
1222                | VarKind::Statement
1223                | VarKind::TryCatch
1224        )
1225    }
1226
1227    pub fn is_callable_or_catch_parameter(&self) -> bool {
1228        matches!(
1229            self.kind,
1230            VarKind::Event
1231                | VarKind::Error
1232                | VarKind::FunctionParam
1233                | VarKind::FunctionTyParam
1234                | VarKind::FunctionReturn
1235                | VarKind::FunctionTyReturn
1236                | VarKind::TryCatch
1237        )
1238    }
1239
1240    pub fn is_local_or_return(&self) -> bool {
1241        self.is_return_parameter()
1242            || (self.is_local_variable() && !self.is_callable_or_catch_parameter())
1243    }
1244
1245    pub fn is_return_parameter(&self) -> bool {
1246        matches!(self.kind, VarKind::FunctionReturn | VarKind::FunctionTyReturn)
1247    }
1248
1249    pub fn is_try_catch_parameter(&self) -> bool {
1250        matches!(self.kind, VarKind::TryCatch)
1251    }
1252
1253    /// Returns `true` if the variable is a state variable.
1254    pub fn is_state_variable(&self) -> bool {
1255        self.kind.is_state()
1256    }
1257
1258    pub fn is_file_level_variable(&self) -> bool {
1259        matches!(self.kind, VarKind::Global)
1260    }
1261
1262    /// Returns `true` if the variable is public.
1263    pub fn is_public(&self) -> bool {
1264        self.visibility >= Some(Visibility::Public)
1265    }
1266}
1267
1268/// The kind of variable.
1269#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
1270pub enum VarKind {
1271    /// Defined at the top level.
1272    Global,
1273    /// Defined in a contract.
1274    State,
1275    /// Defined in a struct.
1276    Struct,
1277    /// Defined in an event.
1278    Event,
1279    /// Defined in an error.
1280    Error,
1281    /// Defined as a function parameter.
1282    FunctionParam,
1283    /// Defined as a function return.
1284    FunctionReturn,
1285    /// Defined as a function type parameter.
1286    FunctionTyParam,
1287    /// Defined as a function type return.
1288    FunctionTyReturn,
1289    /// Defined as a statement, inside of a function, block or `for` statement.
1290    Statement,
1291    /// Defined in a catch clause.
1292    TryCatch,
1293}
1294
1295impl VarKind {
1296    pub fn to_str(self) -> &'static str {
1297        match self {
1298            Self::Global => "file-level variable",
1299            Self::State => "state variable",
1300            Self::Struct => "struct field",
1301            Self::Event => "event parameter",
1302            Self::Error => "error parameter",
1303            Self::FunctionParam | Self::FunctionTyParam => "function parameter",
1304            Self::FunctionReturn | Self::FunctionTyReturn => "function return parameter",
1305            Self::Statement => "variable",
1306            Self::TryCatch => "try/catch clause",
1307        }
1308    }
1309}
1310
1311/// A block of statements.
1312#[derive(Clone, Copy, Debug)]
1313pub struct Block<'hir> {
1314    /// The span of the block, including the `{` and `}`.
1315    pub span: Span,
1316    /// The statements in the block.
1317    pub stmts: &'hir [Stmt<'hir>],
1318}
1319
1320impl<'hir> std::ops::Deref for Block<'hir> {
1321    type Target = [Stmt<'hir>];
1322
1323    fn deref(&self) -> &Self::Target {
1324        self.stmts
1325    }
1326}
1327
1328/// A statement.
1329#[derive(Debug)]
1330pub struct Stmt<'hir> {
1331    /// The statement span.
1332    pub span: Span,
1333    pub kind: StmtKind<'hir>,
1334}
1335
1336/// A kind of statement.
1337#[derive(Debug)]
1338pub enum StmtKind<'hir> {
1339    /// A single-variable declaration statement: `uint256 foo = 42;`.
1340    DeclSingle(VariableId),
1341
1342    /// A multi-variable declaration statement: `(bool success, bytes memory value) = ...;`.
1343    ///
1344    /// Multi-assignments require an expression on the right-hand side.
1345    DeclMulti(&'hir [Option<VariableId>], &'hir Expr<'hir>),
1346
1347    /// A blocked scope: `{ ... }`.
1348    Block(Block<'hir>),
1349
1350    /// An unchecked block: `unchecked { ... }`.
1351    UncheckedBlock(Block<'hir>),
1352
1353    /// An inline assembly block: `assembly { ... }`.
1354    AssemblyBlock(Block<'hir>),
1355
1356    /// An emit statement: `emit Foo.bar(42);`.
1357    ///
1358    /// Always contains an `ExprKind::Call`.
1359    Emit(&'hir Expr<'hir>),
1360
1361    /// A revert statement: `revert Foo.bar(42);`.
1362    ///
1363    /// Always contains an `ExprKind::Call`.
1364    Revert(&'hir Expr<'hir>),
1365
1366    /// A return statement: `return 42;`.
1367    Return(Option<&'hir Expr<'hir>>),
1368
1369    /// A break statement: `break;`.
1370    Break,
1371
1372    /// A continue statement: `continue;`.
1373    Continue,
1374
1375    /// A loop statement. This is desugared from all `for`, `while`, and `do while` statements.
1376    Loop(Block<'hir>, LoopSource),
1377
1378    /// An `if` statement with an optional `else` block: `if (expr) { ... } else { ... }`.
1379    If(&'hir Expr<'hir>, &'hir Stmt<'hir>, Option<&'hir Stmt<'hir>>),
1380
1381    /// A switch statement: `switch expr case 0 { ... } default { ... }`.
1382    Switch(&'hir StmtSwitch<'hir>),
1383
1384    /// A try statement: `try fooBar(42) returns (...) { ... } catch (...) { ... }`.
1385    Try(&'hir StmtTry<'hir>),
1386
1387    /// An expression with a trailing semicolon.
1388    Expr(&'hir Expr<'hir>),
1389
1390    /// A modifier placeholder statement: `_;`.
1391    Placeholder,
1392
1393    Err(ErrorGuaranteed),
1394}
1395
1396/// A switch statement: `switch expr case 0 { ... } default { ... }`.
1397#[derive(Debug)]
1398pub struct StmtSwitch<'hir> {
1399    /// The switch selector.
1400    pub selector: &'hir Expr<'hir>,
1401    /// The cases of the switch statement. Includes the default case in the last position, if any.
1402    pub cases: &'hir [StmtSwitchCase<'hir>],
1403}
1404
1405/// A case of a switch statement.
1406#[derive(Debug)]
1407pub struct StmtSwitchCase<'hir> {
1408    /// The span of the entire case, from `case` or `default` to the closing brace of the block.
1409    pub span: Span,
1410    /// The constant of the case, if any. `None` for the default case.
1411    pub constant: Option<&'hir Lit<'hir>>,
1412    /// The case body.
1413    pub body: Block<'hir>,
1414}
1415
1416/// A try statement: `try fooBar(42) returns (...) { ... } catch (...) { ... }`.
1417///
1418/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.tryStatement>
1419#[derive(Debug)]
1420pub struct StmtTry<'hir> {
1421    /// The call expression.
1422    pub expr: Expr<'hir>,
1423    /// The list of clauses. Never empty.
1424    ///
1425    /// The first item is always the `returns` clause.
1426    pub clauses: &'hir [TryCatchClause<'hir>],
1427}
1428
1429/// Clause of a try/catch block: `returns/catch (...) { ... }`.
1430///
1431/// Includes both the successful case and the unsuccessful cases.
1432/// Names are only allowed for unsuccessful cases.
1433///
1434/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.catchClause>
1435#[derive(Debug)]
1436pub struct TryCatchClause<'hir> {
1437    /// The span of the entire clause, from the `returns` and `catch`
1438    /// keywords, to the closing brace of the block.
1439    pub span: Span,
1440    /// The catch clause name: `Error`, `Panic`, or custom.
1441    pub name: Option<Ident>,
1442    /// The parameter list for the clause.
1443    pub args: &'hir [VariableId],
1444    /// A block of statements
1445    pub block: Block<'hir>,
1446}
1447
1448/// The loop type that yielded an [`StmtKind::Loop`].
1449#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1450pub enum LoopSource {
1451    /// A `for (...) { ... }` loop.
1452    For,
1453    /// A `while (...) { ... }` loop.
1454    While,
1455    /// A `do { ... } while (...);` loop.
1456    DoWhile,
1457}
1458
1459impl LoopSource {
1460    /// Returns the name of the loop source.
1461    pub fn name(self) -> &'static str {
1462        match self {
1463            Self::For => "for",
1464            Self::While => "while",
1465            Self::DoWhile => "do while",
1466        }
1467    }
1468}
1469
1470/// Resolved name.
1471#[derive(Clone, Copy, PartialEq, Eq, Hash, From, EnumIs)]
1472pub enum Res {
1473    /// A resolved item.
1474    Item(ItemId),
1475    /// Synthetic import namespace, X in `import * as X from "path"` or `import "path" as X`.
1476    Namespace(SourceId),
1477    /// A builtin symbol.
1478    Builtin(Builtin),
1479    /// An error occurred while resolving the item. Silences further errors regarding this name.
1480    Err(ErrorGuaranteed),
1481}
1482
1483impl fmt::Debug for Res {
1484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1485        f.write_str("Res::")?;
1486        match self {
1487            Self::Item(id) => write!(f, "Item({id:?})"),
1488            Self::Namespace(id) => write!(f, "Namespace({id:?})"),
1489            Self::Builtin(b) => write!(f, "Builtin({b:?})"),
1490            Self::Err(_) => f.write_str("Err"),
1491        }
1492    }
1493}
1494
1495macro_rules! impl_try_from {
1496    ($($t:ty => $pat:pat => $e:expr),* $(,)?) => {
1497        $(
1498            impl TryFrom<Res> for $t {
1499                type Error = ();
1500
1501                fn try_from(decl: Res) -> Result<Self, ()> {
1502                    match decl {
1503                        $pat => $e,
1504                        _ => Err(()),
1505                    }
1506                }
1507            }
1508        )*
1509    };
1510}
1511
1512impl_try_from!(
1513    ItemId => Res::Item(id) => Ok(id),
1514    ContractId => Res::Item(ItemId::Contract(id)) => Ok(id),
1515    // FunctionId => Res::Item(ItemId::Function(id)) => Ok(id),
1516    EventId => Res::Item(ItemId::Event(id)) => Ok(id),
1517    ErrorId => Res::Item(ItemId::Error(id)) => Ok(id),
1518);
1519
1520impl Res {
1521    pub fn description(&self) -> &'static str {
1522        match self {
1523            Self::Item(item) => item.description(),
1524            Self::Namespace(_) => "namespace",
1525            Self::Builtin(_) => "builtin",
1526            Self::Err(_) => "<error>",
1527        }
1528    }
1529
1530    pub fn matches(&self, other: &Self) -> bool {
1531        match (self, other) {
1532            (Self::Item(a), Self::Item(b)) => a.matches(b),
1533            _ => std::mem::discriminant(self) == std::mem::discriminant(other),
1534        }
1535    }
1536
1537    pub fn as_variable(&self) -> Option<VariableId> {
1538        if let Self::Item(id) = self { id.as_variable() } else { None }
1539    }
1540}
1541
1542/// An expression.
1543#[derive(Debug)]
1544pub struct Expr<'hir> {
1545    pub id: ExprId,
1546    pub kind: ExprKind<'hir>,
1547    /// The expression span.
1548    pub span: Span,
1549}
1550
1551impl Expr<'_> {
1552    /// Peels off unnecessary parentheses from the expression.
1553    pub fn peel_parens(&self) -> &Self {
1554        let mut expr = self;
1555        while let ExprKind::Tuple([Some(inner)]) = expr.kind {
1556            expr = inner;
1557        }
1558        expr
1559    }
1560}
1561
1562/// A kind of expression.
1563#[derive(Debug)]
1564pub enum ExprKind<'hir> {
1565    /// An array literal expression: `[a, b, c, d]`.
1566    Array(&'hir [Expr<'hir>]),
1567
1568    /// An assignment: `a = b`, `a += b`.
1569    Assign(&'hir Expr<'hir>, Option<BinOp>, &'hir Expr<'hir>),
1570
1571    /// A binary operation: `a + b`, `a >> b`.
1572    Binary(&'hir Expr<'hir>, BinOp, &'hir Expr<'hir>),
1573
1574    /// A function call expression: `foo(42)`, `foo({ bar: 42 })`, `foo{ gas: 100_000 }(42)`.
1575    Call(&'hir Expr<'hir>, CallArgs<'hir>, Option<&'hir CallOptions<'hir>>),
1576
1577    // TODO: Add a MethodCall variant
1578    /// A unary `delete` expression: `delete vector`.
1579    Delete(&'hir Expr<'hir>),
1580
1581    /// A resolved symbol: `foo`.
1582    ///
1583    /// Potentially multiple references if it refers to something like an overloaded function.
1584    Ident(&'hir [Res]),
1585
1586    /// A square bracketed indexing expression: `vector[index]`, `MyType[]`.
1587    Index(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>),
1588
1589    /// A square bracketed slice expression: `slice[l:r]`.
1590    Slice(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>),
1591
1592    /// A literal: `hex"1234"`, `5.6 ether`.
1593    Lit(&'hir Lit<'hir>),
1594
1595    /// Access of a named member: `obj.k`.
1596    Member(&'hir Expr<'hir>, Ident),
1597
1598    /// A `new` expression: `new Contract`.
1599    New(Type<'hir>),
1600
1601    /// A `payable` expression: `payable(address(0x...))`.
1602    Payable(&'hir Expr<'hir>),
1603
1604    /// A ternary (AKA conditional) expression: `foo ? bar : baz`.
1605    Ternary(&'hir Expr<'hir>, &'hir Expr<'hir>, &'hir Expr<'hir>),
1606
1607    /// A tuple expression: `(a,,, b, c, d)`.
1608    Tuple(&'hir [Option<&'hir Expr<'hir>>]),
1609
1610    /// A `type()` expression: `type(uint256)`.
1611    TypeCall(Type<'hir>),
1612
1613    /// An elementary type name: `uint256`.
1614    Type(Type<'hir>),
1615
1616    /// A unary operation: `!x`, `-x`, `x++`.
1617    Unary(UnOp, &'hir Expr<'hir>),
1618
1619    /// A dotted Yul identifier path.
1620    YulMember(&'hir Expr<'hir>, Ident),
1621
1622    Err(ErrorGuaranteed),
1623}
1624
1625/// A named argument: `name: value`.
1626#[derive(Debug)]
1627pub struct NamedArg<'hir> {
1628    pub name: Ident,
1629    pub value: Expr<'hir>,
1630}
1631
1632/// Function call options: `foo{ gas: 100_000 }`.
1633#[derive(Clone, Copy, Debug)]
1634pub struct CallOptions<'hir> {
1635    pub span: Span,
1636    pub args: &'hir [NamedArg<'hir>],
1637}
1638
1639/// A list of function call arguments.
1640#[derive(Clone, Copy, Debug)]
1641pub struct CallArgs<'hir> {
1642    /// The span of the arguments. This points to the parenthesized list of arguments.
1643    ///
1644    /// If the list is empty, this points to the empty `()`/`({})` or to where the `(` would be.
1645    pub span: Span,
1646    pub kind: CallArgsKind<'hir>,
1647}
1648
1649impl<'hir> Default for CallArgs<'hir> {
1650    fn default() -> Self {
1651        Self::empty(Span::DUMMY)
1652    }
1653}
1654
1655impl<'hir> CallArgs<'hir> {
1656    /// Creates a new empty list of arguments.
1657    ///
1658    /// `span` should be an empty span.
1659    pub fn empty(span: Span) -> Self {
1660        Self { span, kind: CallArgsKind::empty() }
1661    }
1662
1663    /// Returns `true` if the argument list is not present in the source code.
1664    ///
1665    /// For example, a modifier `m` can be invoked in a function declaration as `m` or `m()`. In the
1666    /// first case, this returns `true`, and the span will point to after `m`. In the second case,
1667    /// this returns `false`.
1668    pub fn is_dummy(&self) -> bool {
1669        self.span.lo() == self.span.hi()
1670    }
1671
1672    /// Returns the length of the arguments.
1673    pub fn len(&self) -> usize {
1674        self.kind.len()
1675    }
1676
1677    /// Returns `true` if the list of arguments is empty.
1678    pub fn is_empty(&self) -> bool {
1679        self.kind.is_empty()
1680    }
1681
1682    /// Returns an iterator over the expressions.
1683    pub fn exprs(
1684        &self,
1685    ) -> impl ExactSizeIterator<Item = &Expr<'hir>> + DoubleEndedIterator + Clone {
1686        self.kind.exprs()
1687    }
1688}
1689
1690/// A list of function call argument expressions.
1691#[derive(Clone, Copy, Debug)]
1692pub enum CallArgsKind<'hir> {
1693    /// A list of unnamed arguments: `(1, 2, 3)`.
1694    Unnamed(&'hir [Expr<'hir>]),
1695
1696    /// A list of named arguments: `({x: 1, y: 2, z: 3})`.
1697    Named(&'hir [NamedArg<'hir>]),
1698}
1699
1700impl Default for CallArgsKind<'_> {
1701    fn default() -> Self {
1702        Self::empty()
1703    }
1704}
1705
1706impl<'hir> CallArgsKind<'hir> {
1707    /// Creates a new empty list of unnamed arguments.
1708    pub fn empty() -> Self {
1709        Self::Unnamed(Default::default())
1710    }
1711
1712    /// Returns the length of the arguments.
1713    pub fn len(&self) -> usize {
1714        match self {
1715            Self::Unnamed(exprs) => exprs.len(),
1716            Self::Named(args) => args.len(),
1717        }
1718    }
1719
1720    /// Returns `true` if the list of arguments is empty.
1721    pub fn is_empty(&self) -> bool {
1722        self.len() == 0
1723    }
1724
1725    /// Returns an iterator over the expressions.
1726    pub fn exprs(
1727        &self,
1728    ) -> impl ExactSizeIterator<Item = &Expr<'hir>> + DoubleEndedIterator + Clone {
1729        match self {
1730            Self::Unnamed(exprs) => Either::Left(exprs.iter()),
1731            Self::Named(args) => Either::Right(args.iter().map(|arg| &arg.value)),
1732        }
1733    }
1734
1735    /// Returns the span of the argument expressions. Does not include the parentheses.
1736    pub fn span(&self) -> Option<Span> {
1737        if self.is_empty() {
1738            return None;
1739        }
1740        Some(Span::join_first_last(self.exprs().map(|e| e.span)))
1741    }
1742}
1743
1744/// A type name.
1745#[derive(Clone, Debug)]
1746pub struct Type<'hir> {
1747    pub span: Span,
1748    pub kind: TypeKind<'hir>,
1749}
1750
1751impl<'hir> Type<'hir> {
1752    /// Dummy placeholder type.
1753    pub const DUMMY: Self =
1754        Self { span: Span::DUMMY, kind: TypeKind::Err(ErrorGuaranteed::new_unchecked()) };
1755
1756    /// Returns `true` if the type is a dummy type.
1757    pub fn is_dummy(&self) -> bool {
1758        self.span == Span::DUMMY && matches!(self.kind, TypeKind::Err(_))
1759    }
1760
1761    pub fn visit<T>(
1762        &self,
1763        hir: &Hir<'hir>,
1764        f: &mut impl FnMut(&Self) -> ControlFlow<T>,
1765    ) -> ControlFlow<T> {
1766        f(self)?;
1767        match self.kind {
1768            TypeKind::Elementary(_) => ControlFlow::Continue(()),
1769            TypeKind::Array(ty) => ty.element.visit(hir, f),
1770            TypeKind::Function(ty) => {
1771                for &param in ty.parameters {
1772                    hir.variable(param).ty.visit(hir, f)?;
1773                }
1774                for &ret in ty.returns {
1775                    hir.variable(ret).ty.visit(hir, f)?;
1776                }
1777                ControlFlow::Continue(())
1778            }
1779            TypeKind::Mapping(ty) => {
1780                ty.key.visit(hir, f)?;
1781                ty.value.visit(hir, f)
1782            }
1783            TypeKind::Custom(_) => ControlFlow::Continue(()),
1784            TypeKind::Err(_) => ControlFlow::Continue(()),
1785        }
1786    }
1787}
1788
1789/// The kind of a type.
1790#[derive(Clone, Debug)]
1791pub enum TypeKind<'hir> {
1792    /// An elementary/primitive type.
1793    Elementary(ElementaryType),
1794
1795    /// `$element[$($size)?]`
1796    Array(&'hir TypeArray<'hir>),
1797    /// `function($($parameters),*) $($attributes)* $(returns ($($returns),+))?`
1798    Function(&'hir TypeFunction<'hir>),
1799    /// `mapping($key $($key_name)? => $value $($value_name)?)`
1800    Mapping(&'hir TypeMapping<'hir>),
1801
1802    /// A custom type name.
1803    Custom(ItemId),
1804
1805    Err(ErrorGuaranteed),
1806}
1807
1808impl TypeKind<'_> {
1809    /// Returns `true` if the type is an elementary type.
1810    pub fn is_elementary(&self) -> bool {
1811        matches!(self, Self::Elementary(_))
1812    }
1813
1814    /// Returns `true` if the type is a reference type.
1815    #[inline]
1816    pub fn is_reference_type(&self) -> bool {
1817        match self {
1818            TypeKind::Elementary(t) => t.is_reference_type(),
1819            TypeKind::Custom(ItemId::Struct(_)) | TypeKind::Array(_) | TypeKind::Mapping(_) => true,
1820            _ => false,
1821        }
1822    }
1823}
1824
1825/// An array type.
1826#[derive(Debug)]
1827pub struct TypeArray<'hir> {
1828    pub element: Type<'hir>,
1829    pub size: Option<&'hir Expr<'hir>>,
1830}
1831
1832/// A function type name.
1833#[derive(Debug)]
1834pub struct TypeFunction<'hir> {
1835    pub parameters: &'hir [VariableId],
1836    pub visibility: Visibility,
1837    pub state_mutability: StateMutability,
1838    pub returns: &'hir [VariableId],
1839}
1840
1841/// A mapping type.
1842#[derive(Debug)]
1843pub struct TypeMapping<'hir> {
1844    pub key: Type<'hir>,
1845    pub key_name: Option<Ident>,
1846    pub value: Type<'hir>,
1847    pub value_name: Option<Ident>,
1848}
1849
1850#[cfg(test)]
1851mod tests {
1852    use super::*;
1853
1854    // Ensure that we track the size of individual HIR nodes.
1855    #[test]
1856    #[cfg_attr(not(target_pointer_width = "64"), ignore = "64-bit only")]
1857    #[cfg_attr(feature = "nightly", ignore = "stable only")]
1858    fn sizes() {
1859        use snapbox::{assert_data_eq, str};
1860        #[track_caller]
1861        fn assert_size<T>(size: impl snapbox::IntoData) {
1862            assert_size_(std::mem::size_of::<T>(), size.into_data());
1863        }
1864        #[track_caller]
1865        fn assert_size_(actual: usize, expected: snapbox::Data) {
1866            assert_data_eq!(actual.to_string(), expected);
1867        }
1868
1869        assert_size::<Hir<'_>>(str!["240"]);
1870
1871        assert_size::<Item<'_, '_>>(str!["16"]);
1872        assert_size::<Contract<'_>>(str!["152"]);
1873        assert_size::<Function<'_>>(str!["144"]);
1874        assert_size::<Struct<'_>>(str!["48"]);
1875        assert_size::<Enum<'_>>(str!["48"]);
1876        assert_size::<Udvt<'_>>(str!["56"]);
1877        assert_size::<Error<'_>>(str!["48"]);
1878        assert_size::<Event<'_>>(str!["56"]);
1879        assert_size::<Variable<'_>>(str!["104"]);
1880
1881        assert_size::<TypeKind<'_>>(str!["16"]);
1882        assert_size::<Type<'_>>(str!["24"]);
1883
1884        assert_size::<ExprKind<'_>>(str!["48"]);
1885        assert_size::<Expr<'_>>(str!["64"]);
1886
1887        assert_size::<StmtKind<'_>>(str!["32"]);
1888        assert_size::<Stmt<'_>>(str!["40"]);
1889        assert_size::<Block<'_>>(str!["24"]);
1890    }
1891}