Skip to main content

miden_assembly_syntax/ast/
import.rs

1use alloc::{sync::Arc, vec::Vec};
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4
5use super::{Ident, Path, Visibility};
6
7/// The explicit source form used by an import declaration.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ImportKind {
10    /// A module import such as `use some::module` or `use some::module as sm`.
11    Module,
12    /// An item import such as `use {foo, bar as baz} from some::module`.
13    Item,
14}
15
16/// A source-level import declaration.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum ImportDecl {
19    Module(ModuleImport),
20    Items(ItemImportGroup),
21}
22
23/// A concrete import recorded in a semantically-analyzed module.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Import {
26    Module(ModuleImport),
27    Item(ItemImport),
28}
29
30/// Imports a foreign module into scope under a local name.
31#[derive(Debug, Clone)]
32pub struct ModuleImport {
33    span: SourceSpan,
34    visibility: Visibility,
35    module_path: Span<Arc<Path>>,
36    local_name: Ident,
37    /// The number of times this import has been used locally.
38    pub uses: usize,
39}
40
41/// Imports one or more foreign items from a module.
42#[derive(Debug, Clone)]
43pub struct ItemImportGroup {
44    span: SourceSpan,
45    visibility: Visibility,
46    module_path: Span<Arc<Path>>,
47    specs: Vec<ImportSpec>,
48}
49
50/// A single item import within an item import group.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct ImportSpec {
53    source_name: Ident,
54    local_name: Ident,
55}
56
57/// Imports a single foreign item from a module into the local scope.
58#[derive(Debug, Clone)]
59pub struct ItemImport {
60    span: SourceSpan,
61    visibility: Visibility,
62    module_path: Span<Arc<Path>>,
63    source_name: Ident,
64    local_name: Ident,
65    /// The number of times this import has been used locally.
66    pub uses: usize,
67}
68
69impl ImportDecl {
70    pub fn kind(&self) -> ImportKind {
71        match self {
72            Self::Module(_) => ImportKind::Module,
73            Self::Items(_) => ImportKind::Item,
74        }
75    }
76
77    pub fn visibility(&self) -> Visibility {
78        match self {
79            Self::Module(import) => import.visibility(),
80            Self::Items(import) => import.visibility(),
81        }
82    }
83
84    pub fn module_path(&self) -> Span<&Path> {
85        match self {
86            Self::Module(import) => import.module_path(),
87            Self::Items(import) => import.module_path(),
88        }
89    }
90}
91
92impl Import {
93    pub fn kind(&self) -> ImportKind {
94        match self {
95            Self::Module(_) => ImportKind::Module,
96            Self::Item(_) => ImportKind::Item,
97        }
98    }
99
100    pub fn visibility(&self) -> Visibility {
101        match self {
102            Self::Module(import) => import.visibility(),
103            Self::Item(import) => import.visibility(),
104        }
105    }
106
107    pub fn module_path(&self) -> Span<&Path> {
108        match self {
109            Self::Module(import) => import.module_path(),
110            Self::Item(import) => import.module_path(),
111        }
112    }
113
114    pub fn local_name(&self) -> &Ident {
115        match self {
116            Self::Module(import) => import.local_name(),
117            Self::Item(import) => import.local_name(),
118        }
119    }
120
121    /// Returns true if this import has at least one use in its containing module.
122    pub fn is_used(&self) -> bool {
123        match self {
124            Self::Module(import) => import.is_used(),
125            Self::Item(import) => import.is_used(),
126        }
127    }
128
129    /// Returns the most specific source span for unused-import diagnostics.
130    pub fn unused_span(&self) -> SourceSpan {
131        match self {
132            Self::Module(import) => import.local_name().span(),
133            Self::Item(import) => import.local_name().span(),
134        }
135    }
136}
137
138impl ModuleImport {
139    pub fn new(
140        span: SourceSpan,
141        visibility: Visibility,
142        module_path: Span<Arc<Path>>,
143        local_name: Ident,
144    ) -> Self {
145        Self {
146            span,
147            visibility,
148            module_path,
149            local_name,
150            uses: 0,
151        }
152    }
153
154    pub fn visibility(&self) -> Visibility {
155        self.visibility
156    }
157
158    pub fn module_path(&self) -> Span<&Path> {
159        self.module_path.as_deref()
160    }
161
162    pub fn set_module_path(&mut self, path: Span<Arc<Path>>) {
163        self.module_path = path;
164    }
165
166    pub fn local_name(&self) -> &Ident {
167        &self.local_name
168    }
169
170    /// Returns true if this import has at least one use in its containing module.
171    pub fn is_used(&self) -> bool {
172        self.uses > 0
173    }
174}
175
176impl ItemImportGroup {
177    pub fn new(
178        span: SourceSpan,
179        visibility: Visibility,
180        module_path: Span<Arc<Path>>,
181        specs: Vec<ImportSpec>,
182    ) -> Self {
183        Self { span, visibility, module_path, specs }
184    }
185
186    pub fn visibility(&self) -> Visibility {
187        self.visibility
188    }
189
190    pub fn module_path(&self) -> Span<&Path> {
191        self.module_path.as_deref()
192    }
193
194    pub fn specs(&self) -> &[ImportSpec] {
195        &self.specs
196    }
197}
198
199impl ImportSpec {
200    pub fn new(source_name: Ident, local_name: Ident) -> Self {
201        Self { source_name, local_name }
202    }
203
204    pub fn source_name(&self) -> &Ident {
205        &self.source_name
206    }
207
208    pub fn local_name(&self) -> &Ident {
209        &self.local_name
210    }
211
212    pub fn is_renamed(&self) -> bool {
213        self.source_name != self.local_name
214    }
215}
216
217impl ItemImport {
218    pub fn new(
219        span: SourceSpan,
220        visibility: Visibility,
221        module_path: Span<Arc<Path>>,
222        source_name: Ident,
223        local_name: Ident,
224    ) -> Self {
225        Self {
226            span,
227            visibility,
228            module_path,
229            source_name,
230            local_name,
231            uses: 0,
232        }
233    }
234
235    pub fn visibility(&self) -> Visibility {
236        self.visibility
237    }
238
239    pub fn module_path(&self) -> Span<&Path> {
240        self.module_path.as_deref()
241    }
242
243    pub fn source_name(&self) -> &Ident {
244        &self.source_name
245    }
246
247    pub fn local_name(&self) -> &Ident {
248        &self.local_name
249    }
250
251    pub fn target_path(&self) -> Span<Arc<Path>> {
252        Span::new(self.source_name.span(), self.module_path.inner().join(&self.source_name).into())
253    }
254
255    /// Returns true if this import has at least one use in its containing module.
256    pub fn is_used(&self) -> bool {
257        self.uses > 0 || self.visibility.is_public()
258    }
259}
260
261impl Spanned for Import {
262    fn span(&self) -> SourceSpan {
263        match self {
264            Self::Module(import) => import.span(),
265            Self::Item(import) => import.span(),
266        }
267    }
268}
269
270impl Spanned for ImportDecl {
271    fn span(&self) -> SourceSpan {
272        match self {
273            Self::Module(import) => import.span(),
274            Self::Items(import) => import.span(),
275        }
276    }
277}
278
279impl Spanned for ModuleImport {
280    fn span(&self) -> SourceSpan {
281        self.span
282    }
283}
284
285impl Spanned for ItemImportGroup {
286    fn span(&self) -> SourceSpan {
287        self.span
288    }
289}
290
291impl Spanned for ImportSpec {
292    fn span(&self) -> SourceSpan {
293        self.source_name.span()
294    }
295}
296
297impl Spanned for ItemImport {
298    fn span(&self) -> SourceSpan {
299        self.span
300    }
301}
302
303impl Eq for ModuleImport {}
304
305impl PartialEq for ModuleImport {
306    fn eq(&self, other: &Self) -> bool {
307        self.visibility == other.visibility
308            && self.module_path.inner() == other.module_path.inner()
309            && self.local_name == other.local_name
310    }
311}
312
313impl Eq for ItemImportGroup {}
314
315impl PartialEq for ItemImportGroup {
316    fn eq(&self, other: &Self) -> bool {
317        self.visibility == other.visibility
318            && self.module_path.inner() == other.module_path.inner()
319            && self.specs == other.specs
320    }
321}
322
323impl Eq for ItemImport {}
324
325impl PartialEq for ItemImport {
326    fn eq(&self, other: &Self) -> bool {
327        self.visibility == other.visibility
328            && self.module_path.inner() == other.module_path.inner()
329            && self.source_name == other.source_name
330            && self.local_name == other.local_name
331    }
332}
333
334impl crate::prettier::PrettyPrint for ImportDecl {
335    fn render(&self) -> crate::prettier::Document {
336        match self {
337            Self::Module(import) => import.render(),
338            Self::Items(import) => import.render(),
339        }
340    }
341}
342
343impl crate::prettier::PrettyPrint for Import {
344    fn render(&self) -> crate::prettier::Document {
345        match self {
346            Self::Module(import) => import.render(),
347            Self::Item(import) => import.render(),
348        }
349    }
350}
351
352impl crate::prettier::PrettyPrint for ModuleImport {
353    fn render(&self) -> crate::prettier::Document {
354        use crate::prettier::*;
355
356        let mut doc = const_text("use") + const_text(" ") + display(self.module_path.inner());
357        if self.module_path.last().is_none_or(|name| name != self.local_name.as_str()) {
358            doc += const_text(" as ") + display(&self.local_name);
359        }
360        doc
361    }
362}
363
364impl crate::prettier::PrettyPrint for ItemImportGroup {
365    fn render(&self) -> crate::prettier::Document {
366        use crate::prettier::*;
367
368        let mut doc = Document::Empty;
369        if self.visibility.is_public() {
370            doc += display(self.visibility) + const_text(" ");
371        }
372        doc += const_text("use {");
373
374        for (index, spec) in self.specs.iter().enumerate() {
375            if index > 0 {
376                doc += const_text(", ");
377            }
378            doc += spec.render();
379        }
380
381        doc + const_text("} from ") + display(self.module_path.inner())
382    }
383}
384
385impl crate::prettier::PrettyPrint for ImportSpec {
386    fn render(&self) -> crate::prettier::Document {
387        use crate::prettier::*;
388
389        let mut doc = display(&self.source_name);
390        if self.is_renamed() {
391            doc += const_text(" as ") + display(&self.local_name);
392        }
393        doc
394    }
395}
396
397impl crate::prettier::PrettyPrint for ItemImport {
398    fn render(&self) -> crate::prettier::Document {
399        use crate::prettier::*;
400
401        let mut doc = Document::Empty;
402        if self.visibility.is_public() {
403            doc += display(self.visibility) + const_text(" ");
404        }
405        doc += const_text("use {") + display(&self.source_name);
406        if self.source_name != self.local_name {
407            doc += const_text(" as ") + display(&self.local_name);
408        }
409        doc + const_text("} from ") + display(self.module_path.inner())
410    }
411}