solar_sema/hir/
mod.rs

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