1use 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
21pub struct Arena {
23 bump: bumpalo::Bump,
24}
25
26impl Arena {
27 pub fn new() -> Self {
29 Self { bump: bumpalo::Bump::new() }
30 }
31
32 pub fn bump(&self) -> &bumpalo::Bump {
34 &self.bump
35 }
36
37 pub fn bump_mut(&mut self) -> &mut bumpalo::Bump {
39 &mut self.bump
40 }
41
42 pub fn allocated_bytes(&self) -> usize {
44 self.bump.allocated_bytes()
45 }
46
47 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#[derive(Debug)]
72pub struct Hir<'hir> {
73 pub(crate) sources: IndexVec<SourceId, Source<'hir>>,
75 pub(crate) contracts: IndexVec<ContractId, Contract<'hir>>,
77 pub(crate) functions: IndexVec<FunctionId, Function<'hir>>,
79 pub(crate) structs: IndexVec<StructId, Struct<'hir>>,
81 pub(crate) enums: IndexVec<EnumId, Enum<'hir>>,
83 pub(crate) udvts: IndexVec<UdvtId, Udvt<'hir>>,
85 pub(crate) events: IndexVec<EventId, Event<'hir>>,
87 pub(crate) errors: IndexVec<ErrorId, Error<'hir>>,
89 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 (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 (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 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 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 #[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 pub fn item_ids(&self) -> impl Iterator<Item = ItemId> + Clone {
204 self.item_ids_vec().into_iter()
205 }
206
207 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 #[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 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 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 pub struct SourceId;
272
273 pub struct ContractId;
275
276 pub struct FunctionId;
278
279 pub struct StructId;
281
282 pub struct EnumId;
284
285 pub struct UdvtId;
287
288 pub struct EventId;
290
291 pub struct ErrorId;
293
294 pub struct VariableId;
296
297 pub struct ExprId;
299}
300
301pub struct Source<'hir> {
303 pub file: Arc<SourceFile>,
304 pub imports: &'hir [(ast::ItemId, SourceId)],
309 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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
425 pub fn is_visible_in_derived_contracts(self) -> bool {
426 self.is_visible_in_contract() && self.visibility() >= Visibility::Internal
427 }
428
429 #[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 #[inline]
441 pub fn is_public(&self) -> bool {
442 self.visibility() >= Visibility::Public
443 }
444
445 #[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 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 #[inline]
506 pub fn matches(&self, other: &Self) -> bool {
507 std::mem::discriminant(self) == std::mem::discriminant(other)
508 }
509
510 pub fn as_contract(&self) -> Option<ContractId> {
512 if let Self::Contract(v) = *self { Some(v) } else { None }
513 }
514
515 pub fn as_function(&self) -> Option<FunctionId> {
517 if let Self::Function(v) = *self { Some(v) } else { None }
518 }
519
520 pub fn as_struct(&self) -> Option<StructId> {
522 if let Self::Struct(v) = *self { Some(v) } else { None }
523 }
524
525 pub fn as_variable(&self) -> Option<VariableId> {
527 if let Self::Variable(v) = *self { Some(v) } else { None }
528 }
529}
530
531#[derive(Debug)]
533pub struct Contract<'hir> {
534 pub source: SourceId,
536 pub span: Span,
538 pub name: Ident,
540 pub kind: ContractKind,
542 pub bases: &'hir [ContractId],
544 pub bases_args: &'hir [Modifier<'hir>],
546 pub linearized_bases: &'hir [ContractId],
552 pub linearized_bases_args: &'hir [Option<&'hir Modifier<'hir>>],
559 pub ctor: Option<FunctionId>,
561 pub fallback: Option<FunctionId>,
563 pub receive: Option<FunctionId>,
565 pub items: &'hir [ItemId],
570}
571
572impl Contract<'_> {
573 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 pub fn functions(&self) -> impl Iterator<Item = FunctionId> + Clone + use<'_> {
584 self.items.iter().filter_map(ItemId::as_function)
585 }
586
587 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 pub fn variables(&self) -> impl Iterator<Item = VariableId> + Clone + use<'_> {
594 self.items.iter().filter_map(ItemId::as_variable)
595 }
596
597 pub fn can_be_deployed(&self) -> bool {
599 matches!(self.kind, ContractKind::Contract | ContractKind::Library)
600 }
601
602 pub fn is_abstract(&self) -> bool {
604 self.kind.is_abstract_contract()
605 }
606
607 pub fn description(&self) -> &'static str {
609 self.kind.to_str()
610 }
611}
612
613#[derive(Clone, Copy, Debug)]
615pub struct Modifier<'hir> {
616 pub span: Span,
618 pub id: ItemId,
620 pub args: CallArgs<'hir>,
622}
623
624#[derive(Debug)]
626pub struct Function<'hir> {
627 pub source: SourceId,
629 pub contract: Option<ContractId>,
631 pub span: Span,
633 pub name: Option<Ident>,
636 pub kind: FunctionKind,
638 pub visibility: Visibility,
640 pub state_mutability: StateMutability,
642 pub modifiers: &'hir [Modifier<'hir>],
644 pub marked_virtual: bool,
646 pub virtual_: bool,
648 pub override_: bool,
650 pub overrides: &'hir [ContractId],
651 pub parameters: &'hir [VariableId],
653 pub returns: &'hir [VariableId],
655 pub body: Option<Block<'hir>>,
657 pub body_span: Span,
659 pub gettee: Option<VariableId>,
661}
662
663impl Function<'_> {
664 pub fn keyword_span(&self) -> Span {
666 self.span.with_hi(self.span.lo() + self.kind.to_str().len() as u32)
667 }
668
669 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 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 pub fn is_special(&self) -> bool {
689 matches!(self.kind, FunctionKind::Receive | FunctionKind::Fallback)
691 }
692
693 pub fn is_constructor(&self) -> bool {
695 matches!(self.kind, FunctionKind::Constructor)
696 }
697
698 pub fn mutates_state(&self) -> bool {
700 self.state_mutability >= StateMutability::Payable
701 }
702
703 pub fn variables(&self) -> impl DoubleEndedIterator<Item = VariableId> + Clone + use<'_> {
705 self.parameters.iter().copied().chain(self.returns.iter().copied())
706 }
707
708 pub fn description(&self) -> &'static str {
710 if self.is_getter() { "getter function" } else { self.kind.to_str() }
711 }
712}
713
714#[derive(Debug)]
716pub struct Struct<'hir> {
717 pub source: SourceId,
719 pub contract: Option<ContractId>,
721 pub span: Span,
723 pub name: Ident,
725 pub fields: &'hir [VariableId],
726}
727
728#[derive(Debug)]
730pub struct Enum<'hir> {
731 pub source: SourceId,
733 pub contract: Option<ContractId>,
735 pub span: Span,
737 pub name: Ident,
739 pub variants: &'hir [Ident],
741}
742
743#[derive(Debug)]
745pub struct Udvt<'hir> {
746 pub source: SourceId,
748 pub contract: Option<ContractId>,
750 pub span: Span,
752 pub name: Ident,
754 pub ty: Type<'hir>,
756}
757
758#[derive(Debug)]
760pub struct Event<'hir> {
761 pub source: SourceId,
763 pub contract: Option<ContractId>,
765 pub span: Span,
767 pub name: Ident,
769 pub anonymous: bool,
771 pub parameters: &'hir [VariableId],
772}
773
774#[derive(Debug)]
776pub struct Error<'hir> {
777 pub source: SourceId,
779 pub contract: Option<ContractId>,
781 pub span: Span,
783 pub name: Ident,
785 pub parameters: &'hir [VariableId],
786}
787
788#[derive(Debug)]
790pub struct Variable<'hir> {
791 pub source: SourceId,
793 pub contract: Option<ContractId>,
795 pub function: Option<FunctionId>,
797 pub span: Span,
799 pub kind: VarKind,
801 pub ty: Type<'hir>,
803 pub name: Option<Ident>,
805 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 pub getter: Option<FunctionId>,
815}
816
817impl<'hir> Variable<'hir> {
818 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 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 pub fn description(&self) -> &'static str {
856 self.kind.to_str()
857 }
858
859 pub fn is_constant(&self) -> bool {
861 self.mutability == Some(VarMut::Constant)
862 }
863
864 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 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 pub fn is_public(&self) -> bool {
932 self.visibility >= Some(Visibility::Public)
933 }
934}
935
936#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
938pub enum VarKind {
939 Global,
941 State,
943 Struct,
945 Event,
947 Error,
949 FunctionParam,
951 FunctionReturn,
953 FunctionTyParam,
955 FunctionTyReturn,
957 Statement,
959 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#[derive(Clone, Copy, Debug)]
981pub struct Block<'hir> {
982 pub span: Span,
984 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#[derive(Debug)]
998pub struct Stmt<'hir> {
999 pub span: Span,
1001 pub kind: StmtKind<'hir>,
1002}
1003
1004#[derive(Debug)]
1006pub enum StmtKind<'hir> {
1007 DeclSingle(VariableId),
1012
1013 DeclMulti(&'hir [Option<VariableId>], &'hir Expr<'hir>),
1017
1018 Block(Block<'hir>),
1020
1021 UncheckedBlock(Block<'hir>),
1023
1024 Emit(&'hir Expr<'hir>),
1028
1029 Revert(&'hir Expr<'hir>),
1033
1034 Return(Option<&'hir Expr<'hir>>),
1036
1037 Break,
1039
1040 Continue,
1042
1043 Loop(Block<'hir>, LoopSource),
1045
1046 If(&'hir Expr<'hir>, &'hir Stmt<'hir>, Option<&'hir Stmt<'hir>>),
1048
1049 Try(&'hir StmtTry<'hir>),
1051
1052 Expr(&'hir Expr<'hir>),
1054
1055 Placeholder,
1057
1058 Err(ErrorGuaranteed),
1059}
1060
1061#[derive(Debug)]
1065pub struct StmtTry<'hir> {
1066 pub expr: Expr<'hir>,
1068 pub clauses: &'hir [TryCatchClause<'hir>],
1072}
1073
1074#[derive(Debug)]
1081pub struct TryCatchClause<'hir> {
1082 pub span: Span,
1085 pub name: Option<Ident>,
1087 pub args: &'hir [VariableId],
1089 pub block: Block<'hir>,
1091}
1092
1093#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1095pub enum LoopSource {
1096 For,
1098 While,
1100 DoWhile,
1102}
1103
1104impl LoopSource {
1105 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#[derive(Clone, Copy, PartialEq, Eq, Hash, From, EnumIs)]
1117pub enum Res {
1118 Item(ItemId),
1120 Namespace(SourceId),
1122 Builtin(Builtin),
1124 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 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#[derive(Debug)]
1189pub struct Expr<'hir> {
1190 pub id: ExprId,
1191 pub kind: ExprKind<'hir>,
1192 pub span: Span,
1194}
1195
1196impl Expr<'_> {
1197 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#[derive(Debug)]
1209pub enum ExprKind<'hir> {
1210 Array(&'hir [Expr<'hir>]),
1212
1213 Assign(&'hir Expr<'hir>, Option<BinOp>, &'hir Expr<'hir>),
1215
1216 Binary(&'hir Expr<'hir>, BinOp, &'hir Expr<'hir>),
1218
1219 Call(&'hir Expr<'hir>, CallArgs<'hir>, Option<&'hir [NamedArg<'hir>]>),
1221
1222 Delete(&'hir Expr<'hir>),
1225
1226 Ident(&'hir [Res]),
1230
1231 Index(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>),
1233
1234 Slice(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>),
1236
1237 Lit(&'hir Lit<'hir>),
1239
1240 Member(&'hir Expr<'hir>, Ident),
1242
1243 New(Type<'hir>),
1245
1246 Payable(&'hir Expr<'hir>),
1248
1249 Ternary(&'hir Expr<'hir>, &'hir Expr<'hir>, &'hir Expr<'hir>),
1251
1252 Tuple(&'hir [Option<&'hir Expr<'hir>>]),
1254
1255 TypeCall(Type<'hir>),
1257
1258 Type(Type<'hir>),
1260
1261 Unary(UnOp, &'hir Expr<'hir>),
1263
1264 Err(ErrorGuaranteed),
1265}
1266
1267#[derive(Debug)]
1269pub struct NamedArg<'hir> {
1270 pub name: Ident,
1271 pub value: Expr<'hir>,
1272}
1273
1274#[derive(Clone, Copy, Debug)]
1276pub struct CallArgs<'hir> {
1277 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 pub fn empty(span: Span) -> Self {
1295 Self { span, kind: CallArgsKind::empty() }
1296 }
1297
1298 pub fn is_dummy(&self) -> bool {
1304 self.span.lo() == self.span.hi()
1305 }
1306
1307 pub fn len(&self) -> usize {
1309 self.kind.len()
1310 }
1311
1312 pub fn is_empty(&self) -> bool {
1314 self.kind.is_empty()
1315 }
1316
1317 pub fn exprs(
1319 &self,
1320 ) -> impl ExactSizeIterator<Item = &Expr<'hir>> + DoubleEndedIterator + Clone {
1321 self.kind.exprs()
1322 }
1323}
1324
1325#[derive(Clone, Copy, Debug)]
1327pub enum CallArgsKind<'hir> {
1328 Unnamed(&'hir [Expr<'hir>]),
1330
1331 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 pub fn empty() -> Self {
1344 Self::Unnamed(Default::default())
1345 }
1346
1347 pub fn len(&self) -> usize {
1349 match self {
1350 Self::Unnamed(exprs) => exprs.len(),
1351 Self::Named(args) => args.len(),
1352 }
1353 }
1354
1355 pub fn is_empty(&self) -> bool {
1357 self.len() == 0
1358 }
1359
1360 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 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#[derive(Clone, Debug)]
1381pub struct Type<'hir> {
1382 pub span: Span,
1383 pub kind: TypeKind<'hir>,
1384}
1385
1386impl<'hir> Type<'hir> {
1387 pub const DUMMY: Self =
1389 Self { span: Span::DUMMY, kind: TypeKind::Err(ErrorGuaranteed::new_unchecked()) };
1390
1391 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 ¶m 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#[derive(Clone, Debug)]
1426pub enum TypeKind<'hir> {
1427 Elementary(ElementaryType),
1429
1430 Array(&'hir TypeArray<'hir>),
1432 Function(&'hir TypeFunction<'hir>),
1434 Mapping(&'hir TypeMapping<'hir>),
1436
1437 Custom(ItemId),
1439
1440 Err(ErrorGuaranteed),
1441}
1442
1443impl TypeKind<'_> {
1444 pub fn is_elementary(&self) -> bool {
1446 matches!(self, Self::Elementary(_))
1447 }
1448
1449 #[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#[derive(Debug)]
1462pub struct TypeArray<'hir> {
1463 pub element: Type<'hir>,
1464 pub size: Option<&'hir Expr<'hir>>,
1465}
1466
1467#[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#[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 #[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}