1use 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
28pub struct Arena {
30 bump: bumpalo::Bump,
31}
32
33impl Arena {
34 pub fn new() -> Self {
36 Self { bump: bumpalo::Bump::new() }
37 }
38
39 pub fn bump(&self) -> &bumpalo::Bump {
41 &self.bump
42 }
43
44 pub fn bump_mut(&mut self) -> &mut bumpalo::Bump {
46 &mut self.bump
47 }
48
49 pub fn allocated_bytes(&self) -> usize {
51 self.bump.allocated_bytes()
52 }
53
54 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#[derive(Debug)]
79pub struct Hir<'hir> {
80 pub(crate) sources: IndexVec<SourceId, Source<'hir>>,
82 pub(crate) docs: IndexVec<DocId, Doc<'hir>>,
84 pub(crate) contracts: IndexVec<ContractId, Contract<'hir>>,
86 pub(crate) functions: IndexVec<FunctionId, Function<'hir>>,
88 pub(crate) structs: IndexVec<StructId, Struct<'hir>>,
90 pub(crate) enums: IndexVec<EnumId, Enum<'hir>>,
92 pub(crate) udvts: IndexVec<UdvtId, Udvt<'hir>>,
94 pub(crate) events: IndexVec<EventId, Event<'hir>>,
96 pub(crate) errors: IndexVec<ErrorId, Error<'hir>>,
98 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 (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 (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 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 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 #[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 pub fn item_ids(&self) -> impl Iterator<Item = ItemId> + Clone {
223 self.item_ids_vec().into_iter()
224 }
225
226 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 #[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 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 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 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
296pub 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 #[inline]
311 pub fn new() -> Self {
312 Self { counter: Cell::new(0) }
313 }
314
315 #[inline]
317 pub fn next<I: Idx>(&self) -> I {
318 I::from_usize(self.next_usize())
319 }
320
321 #[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
330pub struct HirBuilder<'hir, 'id> {
332 arena: &'hir bumpalo::Bump,
333 next_id: &'id IdCounter,
334}
335
336impl<'hir, 'id> HirBuilder<'hir, 'id> {
337 pub fn new(arena: &'hir bumpalo::Bump, next_id: &'id IdCounter) -> Self {
339 Self { arena, next_id }
340 }
341
342 pub fn next_expr_id(&self) -> ExprId {
344 self.next_id.next()
345 }
346
347 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 pub fn expr_kind(&self, kind: ExprKind<'hir>) -> Expr<'hir> {
354 Expr { id: self.next_expr_id(), kind, span: Span::DUMMY }
355 }
356
357 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 pub fn stmt_alloc(&self, kind: StmtKind<'hir>, span: Span) -> &'hir Stmt<'hir> {
364 self.arena.alloc(Stmt { kind, span })
365 }
366
367 pub fn break_stmt(&self, span: Span) -> &'hir Stmt<'hir> {
369 self.stmt_alloc(StmtKind::Break, span)
370 }
371
372 pub fn continue_stmt(&self, span: Span) -> &'hir Stmt<'hir> {
374 self.stmt_alloc(StmtKind::Continue, span)
375 }
376
377 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 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 pub fn lit_expr(&self, lit: &'hir Lit<'hir>) -> ExprKind<'hir> {
394 ExprKind::Lit(lit)
395 }
396
397 pub fn expr_owned(&self, id: ExprId, kind: ExprKind<'hir>, span: Span) -> Expr<'hir> {
399 Expr { id, kind, span }
400 }
401
402 pub fn expr_auto(&self, kind: ExprKind<'hir>, span: Span) -> Expr<'hir> {
404 Expr { id: self.next_expr_id(), kind, span }
405 }
406
407 pub fn stmt(&self, kind: StmtKind<'hir>, span: Span) -> Stmt<'hir> {
409 Stmt { kind, span }
410 }
411
412 pub fn block(&self, stmts: &'hir [Stmt<'hir>], span: Span) -> Block<'hir> {
414 Block { stmts, span }
415 }
416}
417
418newtype_index! {
419 pub struct SourceId;
421
422 pub struct DocId;
426
427 pub struct ContractId;
429
430 pub struct FunctionId;
432
433 pub struct StructId;
435
436 pub struct EnumId;
438
439 pub struct UdvtId;
441
442 pub struct EventId;
444
445 pub struct ErrorId;
447
448 pub struct VariableId;
450
451 pub struct ExprId;
453}
454
455impl DocId {
456 pub const EMPTY: Self = Self::new(0);
460
461 pub fn is_empty(&self) -> bool {
463 self == &Self::EMPTY
464 }
465}
466
467pub struct Source<'hir> {
469 pub file: Arc<SourceFile>,
470 pub imports: &'hir [(ast::ItemId, SourceId)],
475 pub items: &'hir [ItemId],
477 pub usings: &'hir [UsingDirective<'hir>],
479 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#[derive(Debug)]
498pub struct Doc<'hir> {
499 pub source: SourceId,
501 pub item: ItemId,
503 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
635 pub fn is_visible_in_derived_contracts(self) -> bool {
636 self.is_visible_in_contract() && self.visibility() >= Visibility::Internal
637 }
638
639 #[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 #[inline]
651 pub fn is_visible_as_library_member(self) -> bool {
652 self.visibility() >= Visibility::Internal
653 }
654
655 #[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 #[inline]
669 pub fn is_public(&self) -> bool {
670 self.visibility() >= Visibility::Public
671 }
672
673 #[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 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 #[inline]
734 pub fn matches(&self, other: &Self) -> bool {
735 std::mem::discriminant(self) == std::mem::discriminant(other)
736 }
737
738 #[inline]
740 pub fn parent(self, hir: &Hir<'_>) -> Option<Self> {
741 hir.item(self).parent()
742 }
743
744 pub fn as_contract(&self) -> Option<ContractId> {
746 if let Self::Contract(v) = *self { Some(v) } else { None }
747 }
748
749 pub fn as_function(&self) -> Option<FunctionId> {
751 if let Self::Function(v) = *self { Some(v) } else { None }
752 }
753
754 pub fn as_struct(&self) -> Option<StructId> {
756 if let Self::Struct(v) = *self { Some(v) } else { None }
757 }
758
759 pub fn as_variable(&self) -> Option<VariableId> {
761 if let Self::Variable(v) = *self { Some(v) } else { None }
762 }
763}
764
765#[derive(Debug)]
767pub struct Contract<'hir> {
768 pub source: SourceId,
770 pub doc: DocId,
772 pub span: Span,
774 pub name: Ident,
776 pub kind: ContractKind,
778 pub layout: Option<&'hir Expr<'hir>>,
780 pub bases: &'hir [ContractId],
782 pub bases_args: &'hir [Modifier<'hir>],
784 pub linearized_bases: &'hir [ContractId],
790 pub linearized_bases_args: &'hir [Option<&'hir Modifier<'hir>>],
797 pub ctor: Option<FunctionId>,
799 pub fallback: Option<FunctionId>,
801 pub receive: Option<FunctionId>,
803 pub items: &'hir [ItemId],
808 pub usings: &'hir [UsingDirective<'hir>],
810}
811
812impl Contract<'_> {
813 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 pub fn functions(&self) -> impl Iterator<Item = FunctionId> + Clone + use<'_> {
824 self.items.iter().filter_map(ItemId::as_function)
825 }
826
827 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 pub fn variables(&self) -> impl Iterator<Item = VariableId> + Clone + use<'_> {
834 self.items.iter().filter_map(ItemId::as_variable)
835 }
836
837 pub fn can_be_deployed(&self) -> bool {
839 matches!(self.kind, ContractKind::Contract | ContractKind::Library)
840 }
841
842 pub fn is_abstract(&self) -> bool {
844 self.kind.is_abstract_contract()
845 }
846
847 pub fn description(&self) -> &'static str {
849 self.kind.to_str()
850 }
851}
852
853pub fn can_receive_ether(contract: &Contract<'_>, gcx: Gcx<'_>) -> bool {
859 if contract.receive.is_some() {
861 return true;
862 }
863
864 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#[derive(Clone, Copy, Debug)]
877pub struct Modifier<'hir> {
878 pub span: Span,
880 pub id: ItemId,
882 pub args: CallArgs<'hir>,
884}
885
886#[derive(Debug)]
888pub struct UsingDirective<'hir> {
889 pub span: Span,
891 pub source: SourceId,
893 pub contract: Option<ContractId>,
895 pub ty: Option<Type<'hir>>,
897 pub global: bool,
899 pub entries: &'hir [UsingEntry<'hir>],
901}
902
903#[derive(Debug)]
905pub struct UsingEntry<'hir> {
906 pub span: Span,
908 pub name: Option<Symbol>,
910 pub kind: UsingEntryKind<'hir>,
912 pub operator: Option<ast::UserDefinableOperator>,
914}
915
916#[derive(Debug)]
918pub enum UsingEntryKind<'hir> {
919 Library(ContractId),
921 Functions(&'hir [FunctionId]),
923 Err(ErrorGuaranteed),
925}
926
927#[derive(Debug)]
929pub struct Function<'hir> {
930 pub source: SourceId,
932 pub doc: DocId,
934 pub contract: Option<ContractId>,
936 pub span: Span,
938 pub name: Option<Ident>,
941 pub kind: FunctionKind,
943 pub is_yul: bool,
945 pub visibility: Visibility,
947 pub state_mutability: StateMutability,
949 pub modifiers: &'hir [Modifier<'hir>],
951 pub marked_virtual: bool,
953 pub virtual_: bool,
955 pub override_: bool,
957 pub overrides: &'hir [ContractId],
958 pub parameters: &'hir [VariableId],
960 pub returns: &'hir [VariableId],
962 pub body: Option<Block<'hir>>,
964 pub body_span: Span,
966 pub gettee: Option<VariableId>,
968}
969
970impl Function<'_> {
971 pub fn keyword_span(&self) -> Span {
973 self.span.with_hi(self.span.lo() + self.kind.to_str().len() as u32)
974 }
975
976 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 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 pub fn is_special(&self) -> bool {
996 matches!(self.kind, FunctionKind::Receive | FunctionKind::Fallback)
998 }
999
1000 pub fn is_constructor(&self) -> bool {
1002 matches!(self.kind, FunctionKind::Constructor)
1003 }
1004
1005 pub fn mutates_state(&self) -> bool {
1007 self.state_mutability >= StateMutability::Payable
1008 }
1009
1010 pub fn variables(&self) -> impl DoubleEndedIterator<Item = VariableId> + Clone + use<'_> {
1012 self.parameters.iter().copied().chain(self.returns.iter().copied())
1013 }
1014
1015 pub fn description(&self) -> &'static str {
1017 if self.is_getter() { "getter function" } else { self.kind.to_str() }
1018 }
1019}
1020
1021#[derive(Debug)]
1023pub struct Struct<'hir> {
1024 pub source: SourceId,
1026 pub doc: DocId,
1028 pub contract: Option<ContractId>,
1030 pub span: Span,
1032 pub name: Ident,
1034 pub fields: &'hir [VariableId],
1035}
1036
1037#[derive(Debug)]
1039pub struct Enum<'hir> {
1040 pub source: SourceId,
1042 pub doc: DocId,
1044 pub contract: Option<ContractId>,
1046 pub span: Span,
1048 pub name: Ident,
1050 pub variants: &'hir [Ident],
1052}
1053
1054#[derive(Debug)]
1056pub struct Udvt<'hir> {
1057 pub source: SourceId,
1059 pub doc: DocId,
1061 pub contract: Option<ContractId>,
1063 pub span: Span,
1065 pub name: Ident,
1067 pub ty: Type<'hir>,
1069}
1070
1071#[derive(Debug)]
1073pub struct Event<'hir> {
1074 pub source: SourceId,
1076 pub doc: DocId,
1078 pub contract: Option<ContractId>,
1080 pub span: Span,
1082 pub name: Ident,
1084 pub anonymous: bool,
1086 pub parameters: &'hir [VariableId],
1087}
1088
1089#[derive(Debug)]
1091pub struct Error<'hir> {
1092 pub source: SourceId,
1094 pub doc: DocId,
1096 pub contract: Option<ContractId>,
1098 pub span: Span,
1100 pub name: Ident,
1102 pub parameters: &'hir [VariableId],
1103}
1104
1105#[derive(Debug)]
1107pub struct Variable<'hir> {
1108 pub source: SourceId,
1110 pub doc: DocId,
1112 pub contract: Option<ContractId>,
1114 pub parent: Option<ItemId>,
1121 pub span: Span,
1123 pub kind: VarKind,
1125 pub ty: Type<'hir>,
1127 pub name: Option<Ident>,
1129 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 pub getter: Option<FunctionId>,
1139}
1140
1141impl<'hir> Variable<'hir> {
1142 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 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 pub fn description(&self) -> &'static str {
1188 self.kind.to_str()
1189 }
1190
1191 pub fn is_constant(&self) -> bool {
1193 self.mutability == Some(VarMut::Constant)
1194 }
1195
1196 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 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 pub fn is_public(&self) -> bool {
1264 self.visibility >= Some(Visibility::Public)
1265 }
1266}
1267
1268#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIs)]
1270pub enum VarKind {
1271 Global,
1273 State,
1275 Struct,
1277 Event,
1279 Error,
1281 FunctionParam,
1283 FunctionReturn,
1285 FunctionTyParam,
1287 FunctionTyReturn,
1289 Statement,
1291 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#[derive(Clone, Copy, Debug)]
1313pub struct Block<'hir> {
1314 pub span: Span,
1316 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#[derive(Debug)]
1330pub struct Stmt<'hir> {
1331 pub span: Span,
1333 pub kind: StmtKind<'hir>,
1334}
1335
1336#[derive(Debug)]
1338pub enum StmtKind<'hir> {
1339 DeclSingle(VariableId),
1341
1342 DeclMulti(&'hir [Option<VariableId>], &'hir Expr<'hir>),
1346
1347 Block(Block<'hir>),
1349
1350 UncheckedBlock(Block<'hir>),
1352
1353 AssemblyBlock(Block<'hir>),
1355
1356 Emit(&'hir Expr<'hir>),
1360
1361 Revert(&'hir Expr<'hir>),
1365
1366 Return(Option<&'hir Expr<'hir>>),
1368
1369 Break,
1371
1372 Continue,
1374
1375 Loop(Block<'hir>, LoopSource),
1377
1378 If(&'hir Expr<'hir>, &'hir Stmt<'hir>, Option<&'hir Stmt<'hir>>),
1380
1381 Switch(&'hir StmtSwitch<'hir>),
1383
1384 Try(&'hir StmtTry<'hir>),
1386
1387 Expr(&'hir Expr<'hir>),
1389
1390 Placeholder,
1392
1393 Err(ErrorGuaranteed),
1394}
1395
1396#[derive(Debug)]
1398pub struct StmtSwitch<'hir> {
1399 pub selector: &'hir Expr<'hir>,
1401 pub cases: &'hir [StmtSwitchCase<'hir>],
1403}
1404
1405#[derive(Debug)]
1407pub struct StmtSwitchCase<'hir> {
1408 pub span: Span,
1410 pub constant: Option<&'hir Lit<'hir>>,
1412 pub body: Block<'hir>,
1414}
1415
1416#[derive(Debug)]
1420pub struct StmtTry<'hir> {
1421 pub expr: Expr<'hir>,
1423 pub clauses: &'hir [TryCatchClause<'hir>],
1427}
1428
1429#[derive(Debug)]
1436pub struct TryCatchClause<'hir> {
1437 pub span: Span,
1440 pub name: Option<Ident>,
1442 pub args: &'hir [VariableId],
1444 pub block: Block<'hir>,
1446}
1447
1448#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1450pub enum LoopSource {
1451 For,
1453 While,
1455 DoWhile,
1457}
1458
1459impl LoopSource {
1460 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#[derive(Clone, Copy, PartialEq, Eq, Hash, From, EnumIs)]
1472pub enum Res {
1473 Item(ItemId),
1475 Namespace(SourceId),
1477 Builtin(Builtin),
1479 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 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#[derive(Debug)]
1544pub struct Expr<'hir> {
1545 pub id: ExprId,
1546 pub kind: ExprKind<'hir>,
1547 pub span: Span,
1549}
1550
1551impl Expr<'_> {
1552 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#[derive(Debug)]
1564pub enum ExprKind<'hir> {
1565 Array(&'hir [Expr<'hir>]),
1567
1568 Assign(&'hir Expr<'hir>, Option<BinOp>, &'hir Expr<'hir>),
1570
1571 Binary(&'hir Expr<'hir>, BinOp, &'hir Expr<'hir>),
1573
1574 Call(&'hir Expr<'hir>, CallArgs<'hir>, Option<&'hir CallOptions<'hir>>),
1576
1577 Delete(&'hir Expr<'hir>),
1580
1581 Ident(&'hir [Res]),
1585
1586 Index(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>),
1588
1589 Slice(&'hir Expr<'hir>, Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>),
1591
1592 Lit(&'hir Lit<'hir>),
1594
1595 Member(&'hir Expr<'hir>, Ident),
1597
1598 New(Type<'hir>),
1600
1601 Payable(&'hir Expr<'hir>),
1603
1604 Ternary(&'hir Expr<'hir>, &'hir Expr<'hir>, &'hir Expr<'hir>),
1606
1607 Tuple(&'hir [Option<&'hir Expr<'hir>>]),
1609
1610 TypeCall(Type<'hir>),
1612
1613 Type(Type<'hir>),
1615
1616 Unary(UnOp, &'hir Expr<'hir>),
1618
1619 YulMember(&'hir Expr<'hir>, Ident),
1621
1622 Err(ErrorGuaranteed),
1623}
1624
1625#[derive(Debug)]
1627pub struct NamedArg<'hir> {
1628 pub name: Ident,
1629 pub value: Expr<'hir>,
1630}
1631
1632#[derive(Clone, Copy, Debug)]
1634pub struct CallOptions<'hir> {
1635 pub span: Span,
1636 pub args: &'hir [NamedArg<'hir>],
1637}
1638
1639#[derive(Clone, Copy, Debug)]
1641pub struct CallArgs<'hir> {
1642 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 pub fn empty(span: Span) -> Self {
1660 Self { span, kind: CallArgsKind::empty() }
1661 }
1662
1663 pub fn is_dummy(&self) -> bool {
1669 self.span.lo() == self.span.hi()
1670 }
1671
1672 pub fn len(&self) -> usize {
1674 self.kind.len()
1675 }
1676
1677 pub fn is_empty(&self) -> bool {
1679 self.kind.is_empty()
1680 }
1681
1682 pub fn exprs(
1684 &self,
1685 ) -> impl ExactSizeIterator<Item = &Expr<'hir>> + DoubleEndedIterator + Clone {
1686 self.kind.exprs()
1687 }
1688}
1689
1690#[derive(Clone, Copy, Debug)]
1692pub enum CallArgsKind<'hir> {
1693 Unnamed(&'hir [Expr<'hir>]),
1695
1696 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 pub fn empty() -> Self {
1709 Self::Unnamed(Default::default())
1710 }
1711
1712 pub fn len(&self) -> usize {
1714 match self {
1715 Self::Unnamed(exprs) => exprs.len(),
1716 Self::Named(args) => args.len(),
1717 }
1718 }
1719
1720 pub fn is_empty(&self) -> bool {
1722 self.len() == 0
1723 }
1724
1725 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 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#[derive(Clone, Debug)]
1746pub struct Type<'hir> {
1747 pub span: Span,
1748 pub kind: TypeKind<'hir>,
1749}
1750
1751impl<'hir> Type<'hir> {
1752 pub const DUMMY: Self =
1754 Self { span: Span::DUMMY, kind: TypeKind::Err(ErrorGuaranteed::new_unchecked()) };
1755
1756 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 ¶m 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#[derive(Clone, Debug)]
1791pub enum TypeKind<'hir> {
1792 Elementary(ElementaryType),
1794
1795 Array(&'hir TypeArray<'hir>),
1797 Function(&'hir TypeFunction<'hir>),
1799 Mapping(&'hir TypeMapping<'hir>),
1801
1802 Custom(ItemId),
1804
1805 Err(ErrorGuaranteed),
1806}
1807
1808impl TypeKind<'_> {
1809 pub fn is_elementary(&self) -> bool {
1811 matches!(self, Self::Elementary(_))
1812 }
1813
1814 #[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#[derive(Debug)]
1827pub struct TypeArray<'hir> {
1828 pub element: Type<'hir>,
1829 pub size: Option<&'hir Expr<'hir>>,
1830}
1831
1832#[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#[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 #[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}