Skip to main content

solar_ast/ast/
mod.rs

1//! Solidity AST.
2
3use solar_data_structures::{BumpExt, ThinSlice, index::IndexSlice, newtype_index};
4use std::fmt;
5
6pub use crate::token::CommentKind;
7pub use either::{self, Either};
8pub use solar_interface::{Ident, Span, Spanned, Symbol};
9
10mod expr;
11pub use expr::*;
12
13mod item;
14pub use item::*;
15
16mod lit;
17pub use lit::*;
18
19mod natspec;
20pub use natspec::*;
21
22mod path;
23pub use path::*;
24
25mod semver;
26pub use semver::*;
27
28mod stmt;
29pub use stmt::*;
30
31mod ty;
32pub use ty::*;
33
34pub mod yul;
35
36/// AST box. Allocated on the AST arena.
37pub type Box<'ast, T> = &'ast mut T;
38
39/// AST box slice. Allocated on the AST arena.
40pub type BoxSlice<'ast, T> = Box<'ast, ThinSlice<T>>;
41
42/// AST arena allocator.
43pub struct Arena {
44    bump: bumpalo::Bump,
45}
46
47impl Arena {
48    /// Creates a new AST arena.
49    pub fn new() -> Self {
50        Self { bump: bumpalo::Bump::new() }
51    }
52
53    /// Returns a reference to the arena's bump allocator.
54    pub fn bump(&self) -> &bumpalo::Bump {
55        &self.bump
56    }
57
58    /// Returns a mutable reference to the arena's bump allocator.
59    pub fn bump_mut(&mut self) -> &mut bumpalo::Bump {
60        &mut self.bump
61    }
62
63    /// Calculates the number of bytes currently allocated in the entire arena.
64    pub fn allocated_bytes(&self) -> usize {
65        self.bump.allocated_bytes()
66    }
67
68    /// Returns the number of bytes currently in use.
69    pub fn used_bytes(&self) -> usize {
70        self.bump.used_bytes()
71    }
72}
73
74impl Default for Arena {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl std::ops::Deref for Arena {
81    type Target = bumpalo::Bump;
82
83    #[inline]
84    fn deref(&self) -> &Self::Target {
85        &self.bump
86    }
87}
88
89/// A list of doc-comments.
90#[derive(Default)]
91pub struct DocComments<'ast>(BoxSlice<'ast, DocComment<'ast>>);
92
93impl<'ast> std::ops::Deref for DocComments<'ast> {
94    type Target = BoxSlice<'ast, DocComment<'ast>>;
95
96    #[inline]
97    fn deref(&self) -> &Self::Target {
98        &self.0
99    }
100}
101
102impl std::ops::DerefMut for DocComments<'_> {
103    #[inline]
104    fn deref_mut(&mut self) -> &mut Self::Target {
105        &mut self.0
106    }
107}
108
109impl<'ast> From<BoxSlice<'ast, DocComment<'ast>>> for DocComments<'ast> {
110    fn from(comments: BoxSlice<'ast, DocComment<'ast>>) -> Self {
111        Self(comments)
112    }
113}
114
115impl fmt::Debug for DocComments<'_> {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        f.write_str("DocComments")?;
118        self.0.fmt(f)
119    }
120}
121
122impl DocComments<'_> {
123    /// Returns the span containing all doc-comments.
124    pub fn span(&self) -> Span {
125        Span::join_first_last(self.iter().map(|d| d.span))
126    }
127}
128
129/// A Solidity source file.
130pub struct SourceUnit<'ast> {
131    /// The source unit's items.
132    pub items: Box<'ast, IndexSlice<ItemId, [Item<'ast>]>>,
133}
134
135impl fmt::Debug for SourceUnit<'_> {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.write_str("SourceUnit")?;
138        self.items.fmt(f)
139    }
140}
141
142impl<'ast> SourceUnit<'ast> {
143    /// Creates a new source unit from the given items.
144    pub fn new(items: BoxSlice<'ast, Item<'ast>>) -> Self {
145        Self { items: IndexSlice::from_slice_mut(items) }
146    }
147
148    /// Counts the number of contracts in the source unit.
149    pub fn count_contracts(&self) -> usize {
150        self.items.iter().filter(|item| matches!(item.kind, ItemKind::Contract(_))).count()
151    }
152
153    /// Returns an iterator over the source unit's imports.
154    pub fn imports(&self) -> impl Iterator<Item = (Span, &ImportDirective<'ast>)> {
155        self.items.iter().filter_map(|item| match &item.kind {
156            ItemKind::Import(import) => Some((item.span, import)),
157            _ => None,
158        })
159    }
160}
161
162newtype_index! {
163    /// A [source unit item](Item) ID. Only used in [`SourceUnit`].
164    pub struct ItemId;
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn no_drop() {
173        #[track_caller]
174        fn assert_no_drop<T>() {
175            assert!(!std::mem::needs_drop::<T>(), "{}", std::any::type_name::<T>());
176        }
177        assert_no_drop::<Type<'_>>();
178        assert_no_drop::<Expr<'_>>();
179        assert_no_drop::<Stmt<'_>>();
180        assert_no_drop::<Item<'_>>();
181        assert_no_drop::<SourceUnit<'_>>();
182    }
183
184    // Ensure that we track the size of individual AST nodes.
185    #[test]
186    #[cfg_attr(not(target_pointer_width = "64"), ignore = "64-bit only")]
187    #[cfg_attr(feature = "nightly", ignore = "stable only")]
188    fn sizes() {
189        use snapbox::{assert_data_eq, str};
190        #[track_caller]
191        fn assert_size<T>(size: impl snapbox::IntoData) {
192            assert_size_(std::mem::size_of::<T>(), size.into_data());
193        }
194        #[track_caller]
195        fn assert_size_(actual: usize, expected: snapbox::Data) {
196            assert_data_eq!(actual.to_string(), expected);
197        }
198
199        assert_size::<Span>(str!["8"]);
200        assert_size::<DocComments<'_>>(str!["8"]);
201
202        assert_size::<SourceUnit<'_>>(str!["16"]);
203
204        assert_size::<PragmaDirective<'_>>(str!["32"]);
205        assert_size::<ImportDirective<'_>>(str!["32"]);
206        assert_size::<UsingDirective<'_>>(str!["48"]);
207        assert_size::<ItemContract<'_>>(str!["48"]);
208        assert_size::<ItemFunction<'_>>(str!["144"]);
209        assert_size::<VariableDefinition<'_>>(str!["72"]);
210        assert_size::<ItemStruct<'_>>(str!["24"]);
211        assert_size::<ItemEnum<'_>>(str!["24"]);
212        assert_size::<ItemUdvt<'_>>(str!["40"]);
213        assert_size::<ItemError<'_>>(str!["32"]);
214        assert_size::<ItemEvent<'_>>(str!["32"]);
215        assert_size::<ItemKind<'_>>(str!["144"]);
216        assert_size::<Item<'_>>(str!["160"]);
217
218        assert_size::<FunctionHeader<'_>>(str!["112"]);
219        assert_size::<ParameterList<'_>>(str!["16"]);
220
221        assert_size::<ElementaryType>(str!["4"]);
222        assert_size::<TypeKind<'_>>(str!["16"]);
223        assert_size::<Type<'_>>(str!["24"]);
224
225        assert_size::<ExprKind<'_>>(str!["40"]);
226        assert_size::<Expr<'_>>(str!["48"]);
227
228        assert_size::<StmtKind<'_>>(str!["48"]);
229        assert_size::<Stmt<'_>>(str!["64"]);
230        assert_size::<Block<'_>>(str!["16"]);
231
232        assert_size::<yul::ExprCall<'_>>(str!["24"]);
233        assert_size::<yul::ExprKind<'_>>(str!["32"]);
234        assert_size::<yul::Expr<'_>>(str!["40"]);
235
236        assert_size::<yul::StmtKind<'_>>(str!["56"]);
237        assert_size::<yul::Stmt<'_>>(str!["72"]);
238        assert_size::<yul::Block<'_>>(str!["16"]);
239        assert_size::<yul::StmtFor<'_>>(str!["88"]);
240    }
241}