solar_ast/ast/
mod.rs

1//! Solidity AST.
2
3use solar_data_structures::{BumpExt, 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 path;
20pub use path::*;
21
22mod semver;
23pub use semver::*;
24
25mod stmt;
26pub use stmt::*;
27
28mod ty;
29pub use ty::*;
30
31pub mod yul;
32
33pub type Box<'ast, T> = &'ast mut T;
34
35/// AST arena allocator.
36pub struct Arena {
37    bump: bumpalo::Bump,
38}
39
40impl Arena {
41    /// Creates a new AST arena.
42    pub fn new() -> Self {
43        Self { bump: bumpalo::Bump::new() }
44    }
45
46    /// Returns a reference to the arena's bump allocator.
47    pub fn bump(&self) -> &bumpalo::Bump {
48        &self.bump
49    }
50
51    /// Returns a mutable reference to the arena's bump allocator.
52    pub fn bump_mut(&mut self) -> &mut bumpalo::Bump {
53        &mut self.bump
54    }
55
56    /// Calculates the number of bytes currently allocated in the entire arena.
57    pub fn allocated_bytes(&self) -> usize {
58        self.bump.allocated_bytes()
59    }
60
61    /// Returns the number of bytes currently in use.
62    pub fn used_bytes(&self) -> usize {
63        self.bump.used_bytes()
64    }
65}
66
67impl Default for Arena {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73impl std::ops::Deref for Arena {
74    type Target = bumpalo::Bump;
75
76    #[inline]
77    fn deref(&self) -> &Self::Target {
78        &self.bump
79    }
80}
81
82/// A list of doc-comments.
83#[derive(Default)]
84pub struct DocComments<'ast>(pub Box<'ast, [DocComment]>);
85
86impl<'ast> std::ops::Deref for DocComments<'ast> {
87    type Target = Box<'ast, [DocComment]>;
88
89    #[inline]
90    fn deref(&self) -> &Self::Target {
91        &self.0
92    }
93}
94
95impl std::ops::DerefMut for DocComments<'_> {
96    #[inline]
97    fn deref_mut(&mut self) -> &mut Self::Target {
98        &mut self.0
99    }
100}
101
102impl<'ast> From<Box<'ast, [DocComment]>> for DocComments<'ast> {
103    fn from(comments: Box<'ast, [DocComment]>) -> Self {
104        Self(comments)
105    }
106}
107
108impl fmt::Debug for DocComments<'_> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.write_str("DocComments")?;
111        self.0.fmt(f)
112    }
113}
114
115impl DocComments<'_> {
116    /// Returns the span containing all doc-comments.
117    pub fn span(&self) -> Span {
118        Span::join_first_last(self.iter().map(|d| d.span))
119    }
120}
121
122/// A single doc-comment: `/// foo`, `/** bar */`.
123#[derive(Clone, Copy, Debug)]
124pub struct DocComment {
125    /// The comment kind.
126    pub kind: CommentKind,
127    /// The comment's span including its "quotes" (`//`, `/**`).
128    pub span: Span,
129    /// The comment's contents excluding its "quotes" (`//`, `/**`)
130    /// similarly to symbols in string literal tokens.
131    pub symbol: Symbol,
132}
133
134/// A Solidity source file.
135pub struct SourceUnit<'ast> {
136    /// The source unit's items.
137    pub items: Box<'ast, IndexSlice<ItemId, [Item<'ast>]>>,
138}
139
140impl fmt::Debug for SourceUnit<'_> {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        f.write_str("SourceUnit")?;
143        self.items.fmt(f)
144    }
145}
146
147impl<'ast> SourceUnit<'ast> {
148    /// Creates a new source unit from the given items.
149    pub fn new(items: Box<'ast, [Item<'ast>]>) -> Self {
150        Self { items: IndexSlice::from_slice_mut(items) }
151    }
152
153    /// Counts the number of contracts in the source unit.
154    pub fn count_contracts(&self) -> usize {
155        self.items.iter().filter(|item| matches!(item.kind, ItemKind::Contract(_))).count()
156    }
157
158    /// Returns an iterator over the source unit's imports.
159    pub fn imports(&self) -> impl Iterator<Item = (Span, &ImportDirective<'ast>)> {
160        self.items.iter().filter_map(|item| match &item.kind {
161            ItemKind::Import(import) => Some((item.span, import)),
162            _ => None,
163        })
164    }
165}
166
167newtype_index! {
168    /// A [source unit item](Item) ID. Only used in [`SourceUnit`].
169    pub struct ItemId;
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn no_drop() {
178        #[track_caller]
179        fn assert_no_drop<T>() {
180            assert!(!std::mem::needs_drop::<T>(), "{}", std::any::type_name::<T>());
181        }
182        assert_no_drop::<Type<'_>>();
183        assert_no_drop::<Expr<'_>>();
184        assert_no_drop::<Stmt<'_>>();
185        assert_no_drop::<Item<'_>>();
186        assert_no_drop::<SourceUnit<'_>>();
187    }
188}