Skip to main content

miden_assembly_syntax/ast/
module.rs

1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_core::{
5    advice::AdviceMap,
6    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use miden_debug_types::{SourceFile, SourceManager, SourceSpan, Span, Spanned};
9use miden_utils_diagnostics::Report;
10#[cfg(feature = "arbitrary")]
11use proptest::prelude::*;
12use smallvec::SmallVec;
13
14use super::{
15    Alias, Constant, DocString, EnumType, Export, FunctionType, GlobalItemIndex, ItemIndex,
16    LocalSymbolResolver, Path, Procedure, ProcedureName, QualifiedProcedureName, SymbolResolution,
17    SymbolResolutionError, TypeAlias, TypeDecl, TypeResolver, Variant,
18};
19use crate::{
20    PathBuf,
21    ast::{self, Ident, types},
22    parser::ModuleParser,
23    sema::{LimitKind, SemanticAnalysisError},
24};
25
26// MODULE KIND
27// ================================================================================================
28
29/// Represents the kind of a [Module].
30///
31/// The three different kinds have slightly different rules on what syntax is allowed, as well as
32/// what operations can be performed in the body of procedures defined in the module. See the
33/// documentation for each variant for a summary of these differences.
34#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
35#[cfg_attr(
36    all(feature = "arbitrary", test),
37    miden_test_serde_macros::serde_test(binary_serde(true), serde_test(false))
38)]
39#[repr(u8)]
40pub enum ModuleKind {
41    /// A library is a simple container of code that must be included into an executable module to
42    /// form a complete program.
43    ///
44    /// Library modules cannot use the `begin`..`end` syntax, which is used to define the
45    /// entrypoint procedure for an executable. Aside from this, they are free to use all other
46    /// MASM syntax.
47    #[default]
48    Library = 0,
49    /// An executable is the root module of a program, and provides the entrypoint for executing
50    /// that program.
51    ///
52    /// As the executable module is the root module, it may not export procedures for other modules
53    /// to depend on, it may only import and call externally-defined procedures, or private
54    /// locally-defined procedures.
55    ///
56    /// An executable module must contain a `begin`..`end` block.
57    Executable = 1,
58    /// A kernel is like a library module, but is special in a few ways:
59    ///
60    /// * Its code always executes in the root context, so it is stateful in a way that normal
61    ///   libraries cannot replicate. This can be used to provide core services that would otherwise
62    ///   not be possible to implement.
63    ///
64    /// * The procedures exported from the kernel may be the target of the `syscall` instruction,
65    ///   and in fact _must_ be called that way.
66    Kernel = 2,
67}
68
69impl ModuleKind {
70    pub fn is_executable(&self) -> bool {
71        matches!(self, Self::Executable)
72    }
73
74    pub fn is_kernel(&self) -> bool {
75        matches!(self, Self::Kernel)
76    }
77
78    pub fn is_library(&self) -> bool {
79        matches!(self, Self::Library)
80    }
81}
82
83impl fmt::Display for ModuleKind {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        match self {
86            Self::Library => f.write_str("library"),
87            Self::Executable => f.write_str("executable"),
88            Self::Kernel => f.write_str("kernel"),
89        }
90    }
91}
92
93#[cfg(feature = "arbitrary")]
94impl Arbitrary for ModuleKind {
95    type Parameters = ();
96    type Strategy = BoxedStrategy<Self>;
97
98    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
99        any::<u8>()
100            .prop_map(|tag| match tag % 3 {
101                0 => Self::Library,
102                1 => Self::Executable,
103                _ => Self::Kernel,
104            })
105            .boxed()
106    }
107}
108
109impl Serializable for ModuleKind {
110    fn write_into<W: ByteWriter>(&self, target: &mut W) {
111        target.write_u8(*self as u8)
112    }
113}
114
115impl Deserializable for ModuleKind {
116    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
117        match source.read_u8()? {
118            0 => Ok(Self::Library),
119            1 => Ok(Self::Executable),
120            2 => Ok(Self::Kernel),
121            n => Err(DeserializationError::InvalidValue(format!("invalid module kind tag: {n}"))),
122        }
123    }
124}
125
126// MODULE
127// ================================================================================================
128
129/// The abstract syntax tree for a single Miden Assembly module.
130///
131/// All module kinds share this AST representation, as they are largely identical. However, the
132/// [ModuleKind] dictates how the parsed module is semantically analyzed and validated.
133#[derive(Clone)]
134pub struct Module {
135    /// The span covering the entire definition of this module.
136    span: SourceSpan,
137    /// The documentation associated with this module.
138    ///
139    /// Module documentation is provided in Miden Assembly as a documentation comment starting on
140    /// the first line of the module. All other documentation comments are attached to the item the
141    /// precede in the module body.
142    docs: Option<DocString>,
143    /// The fully-qualified path representing the name of this module.
144    path: PathBuf,
145    /// The kind of module this represents.
146    kind: ModuleKind,
147    /// The items (defined or re-exported) in the module body.
148    pub(crate) items: Vec<Export>,
149    /// AdviceMap that this module expects to be loaded in the host before executing.
150    pub(crate) advice_map: AdviceMap,
151}
152
153/// Constants
154impl Module {
155    /// File extension for a Assembly Module.
156    pub const FILE_EXTENSION: &'static str = "masm";
157
158    /// Name of the root module.
159    pub const ROOT: &'static str = "mod";
160
161    /// File name of the root module.
162    pub const ROOT_FILENAME: &'static str = "mod.masm";
163}
164
165/// Construction
166impl Module {
167    /// Creates a new [Module] with the specified `kind` and fully-qualified path, e.g.
168    /// `std::math::u64`.
169    pub fn new(kind: ModuleKind, path: impl AsRef<Path>) -> Self {
170        let path = path.as_ref().to_absolute().into_owned();
171        Self {
172            span: Default::default(),
173            docs: None,
174            path,
175            kind,
176            items: Default::default(),
177            advice_map: Default::default(),
178        }
179    }
180
181    /// An alias for creating the default, but empty, `#kernel` [Module].
182    pub fn new_kernel() -> Self {
183        Self::new(ModuleKind::Kernel, Path::kernel_path())
184    }
185
186    /// An alias for creating the default, but empty, `$exec` [Module].
187    pub fn new_executable() -> Self {
188        Self::new(ModuleKind::Executable, Path::exec_path())
189    }
190
191    /// Specifies the source span in the source file in which this module was defined, that covers
192    /// the full definition of this module.
193    pub fn with_span(mut self, span: SourceSpan) -> Self {
194        self.span = span;
195        self
196    }
197
198    /// Sets the [Path] for this module
199    pub fn set_path(&mut self, path: impl AsRef<Path>) {
200        self.path = path.as_ref().to_path_buf();
201    }
202
203    /// Modifies the path of this module by overriding the portion of the path preceding
204    /// [`Self::name`], i.e. the portion returned by [`Self::parent`].
205    ///
206    /// See [`PathBuf::set_parent`] for details.
207    pub fn set_parent(&mut self, ns: impl AsRef<Path>) {
208        self.path.set_parent(ns.as_ref());
209    }
210
211    /// Sets the documentation for this module
212    pub fn set_docs(&mut self, docs: Option<Span<String>>) {
213        self.docs = docs.map(DocString::new);
214    }
215
216    /// Like [Module::with_span], but does not require ownership of the [Module].
217    pub fn set_span(&mut self, span: SourceSpan) {
218        self.span = span;
219    }
220
221    fn ensure_item_capacity(&self, span: SourceSpan) -> Result<(), SemanticAnalysisError> {
222        if self.items.len() >= ItemIndex::MAX_ITEMS {
223            return Err(SemanticAnalysisError::LimitExceeded { span, kind: LimitKind::Items });
224        }
225
226        Ok(())
227    }
228
229    pub(crate) fn push_export(&mut self, item: Export) -> Result<(), SemanticAnalysisError> {
230        self.ensure_item_capacity(item.span())?;
231        self.items.push(item);
232        Ok(())
233    }
234
235    /// Defines a constant, raising an error if the constant conflicts with a previous definition
236    pub fn define_constant(&mut self, constant: Constant) -> Result<(), SemanticAnalysisError> {
237        if let Some(prev) = self.items.iter().find(|item| item.name() == &constant.name) {
238            return Err(SemanticAnalysisError::SymbolConflict {
239                span: constant.span,
240                prev_span: prev.span(),
241            });
242        }
243        self.push_export(Export::Constant(constant))?;
244        Ok(())
245    }
246
247    /// Defines a type alias, raising an error if the alias conflicts with a previous definition
248    pub fn define_type(&mut self, ty: TypeAlias) -> Result<(), SemanticAnalysisError> {
249        if let Some(prev) = self.items.iter().find(|item| item.name() == ty.name()) {
250            return Err(SemanticAnalysisError::SymbolConflict {
251                span: ty.span(),
252                prev_span: prev.span(),
253            });
254        }
255        self.push_export(Export::Type(ty.into()))?;
256        Ok(())
257    }
258
259    /// Define a new enum type `ty` with `visibility`
260    ///
261    /// Returns `Err` if:
262    ///
263    /// * A type alias with the same name as the enum type is already defined
264    /// * Two or more variants of the given enum type have the same name
265    /// * A constant (including those implicitly defined by variants of other enums in this module)
266    ///   with the same name as any of the variants of the given enum type, is already defined
267    /// * The concrete type of the enumeration is not an integral type
268    pub fn define_enum(&mut self, ty: EnumType) -> Result<(), SemanticAnalysisError> {
269        let repr = ty.ty().clone();
270
271        if !repr.is_integer() {
272            return Err(SemanticAnalysisError::InvalidEnumRepr { span: ty.span() });
273        }
274
275        // We only define constants for C-like enums
276        if !ty.is_c_like() {
277            self.items.push(Export::Type(ty.into()));
278            return Ok(());
279        }
280
281        let export = ty.clone();
282
283        let (alias, variants) = ty.into_parts();
284
285        if let Some(prev) = self.items.iter().find(|t| t.name() == &alias.name) {
286            return Err(SemanticAnalysisError::SymbolConflict {
287                span: alias.span(),
288                prev_span: prev.span(),
289            });
290        }
291
292        let mut values = SmallVec::<[Span<u64>; 8]>::new_const();
293
294        for variant in variants {
295            // Validate that the discriminant value is unique amongst all variants
296            let value = match &variant.discriminant {
297                ast::ConstantExpr::Int(value) => (*value).map(|v| v.as_int()),
298                expr => {
299                    return Err(SemanticAnalysisError::InvalidEnumDiscriminant {
300                        span: expr.span(),
301                        repr,
302                    });
303                },
304            };
305            if let Some(prev) = values.iter().find(|v| *v == &value) {
306                return Err(SemanticAnalysisError::EnumDiscriminantConflict {
307                    span: value.span(),
308                    prev: prev.span(),
309                });
310            } else {
311                values.push(value);
312            }
313
314            // Validate that the discriminant is a valid instance of the `repr` type
315            variant.assert_instance_of(&repr)?;
316
317            let Variant {
318                span,
319                docs,
320                name,
321                value_ty: _,
322                discriminant,
323            } = variant;
324
325            self.define_constant(Constant {
326                span,
327                docs,
328                visibility: alias.visibility(),
329                name,
330                value: discriminant,
331            })?;
332        }
333
334        self.push_export(Export::Type(export.into()))?;
335
336        Ok(())
337    }
338
339    /// Defines a procedure, raising an error if the procedure is invalid, or conflicts with a
340    /// previous definition
341    pub fn define_procedure(
342        &mut self,
343        procedure: Procedure,
344        _source_manager: Arc<dyn SourceManager>,
345    ) -> Result<(), SemanticAnalysisError> {
346        if let Some(prev) =
347            self.items.iter().find(|item| item.name().as_str() == procedure.name().as_str())
348        {
349            return Err(SemanticAnalysisError::SymbolConflict {
350                span: procedure.span(),
351                prev_span: prev.name().span(),
352            });
353        }
354        self.push_export(Export::Procedure(procedure))?;
355        Ok(())
356    }
357
358    /// Defines an item alias, raising an error if the alias is invalid, or conflicts with a
359    /// previous definition
360    pub fn define_alias(
361        &mut self,
362        item: Alias,
363        _source_manager: Arc<dyn SourceManager>,
364    ) -> Result<(), SemanticAnalysisError> {
365        if self.is_kernel() && item.visibility().is_public() {
366            return Err(SemanticAnalysisError::ReexportFromKernel { span: item.span() });
367        }
368        if let Some(prev) = self
369            .items
370            .iter()
371            .find(|existing| existing.name().as_str() == item.name().as_str())
372        {
373            return Err(SemanticAnalysisError::SymbolConflict {
374                span: item.name().span(),
375                prev_span: prev.name().span(),
376            });
377        }
378        self.push_export(Export::Alias(item))?;
379        Ok(())
380    }
381}
382
383/// Parsing
384impl Module {
385    /// Parse a [Module], `name`, of the given [ModuleKind], from `source_file`.
386    pub fn parse(
387        name: impl AsRef<Path>,
388        kind: ModuleKind,
389        source_file: Arc<SourceFile>,
390        source_manager: Arc<dyn SourceManager>,
391    ) -> Result<Box<Self>, Report> {
392        let name = name.as_ref();
393        let mut parser = Self::parser(kind);
394        parser.parse(name, source_file, source_manager)
395    }
396
397    /// Get a [ModuleParser] for parsing modules of the provided [ModuleKind]
398    pub fn parser(kind: ModuleKind) -> ModuleParser {
399        ModuleParser::new(kind)
400    }
401}
402
403/// Metadata
404impl Module {
405    /// Get the name of this specific module, i.e. the last component of the [Path] that
406    /// represents the fully-qualified name of the module, e.g. `u64` in `std::math::u64`
407    pub fn name(&self) -> &str {
408        self.path.last().expect("non-empty module path")
409    }
410
411    /// Get the fully-qualified name of this module, e.g. `std::math::u64`
412    pub fn path(&self) -> &Path {
413        &self.path
414    }
415
416    /// Get the path of the parent module of this module, e.g. `std::math` in `std::math::u64`
417    pub fn parent(&self) -> Option<&Path> {
418        self.path.parent()
419    }
420
421    /// Returns true if this module belongs to the provided namespace.
422    pub fn is_in_namespace(&self, namespace: &Path) -> bool {
423        self.path.starts_with(namespace)
424    }
425
426    /// Get the module documentation for this module, if it was present in the source code the
427    /// module was parsed from
428    pub fn docs(&self) -> Option<Span<&str>> {
429        self.docs.as_ref().map(|spanned| spanned.as_spanned_str())
430    }
431
432    /// Get the type of module this represents:
433    ///
434    /// See [ModuleKind] for details on the different types of modules.
435    pub fn kind(&self) -> ModuleKind {
436        self.kind
437    }
438
439    /// Override the type of module this represents.
440    ///
441    /// See [ModuleKind] for details on what the different types are.
442    pub fn set_kind(&mut self, kind: ModuleKind) {
443        self.kind = kind;
444    }
445
446    /// Returns true if this module is an executable module.
447    #[inline(always)]
448    pub fn is_executable(&self) -> bool {
449        self.kind.is_executable()
450    }
451
452    /// Returns true if this module is the top-level kernel module.
453    #[inline(always)]
454    pub fn is_kernel(&self) -> bool {
455        self.kind.is_kernel() && self.path.is_kernel_path()
456    }
457
458    /// Returns true if this module is a kernel module.
459    #[inline(always)]
460    pub fn is_in_kernel(&self) -> bool {
461        self.kind.is_kernel()
462    }
463
464    /// Returns true if this module has an entrypoint procedure defined,
465    /// i.e. a `begin`..`end` block.
466    pub fn has_entrypoint(&self) -> bool {
467        self.index_of(Export::is_main).is_some()
468    }
469
470    /// Returns a reference to the advice map derived from this module
471    pub fn advice_map(&self) -> &AdviceMap {
472        &self.advice_map
473    }
474
475    /// Get an iterator over the constants defined in this module.
476    pub fn constants(&self) -> impl Iterator<Item = &Constant> + '_ {
477        self.items.iter().filter_map(|item| match item {
478            Export::Constant(item) => Some(item),
479            _ => None,
480        })
481    }
482
483    /// Same as [Module::constants], but returns mutable references.
484    pub fn constants_mut(&mut self) -> impl Iterator<Item = &mut Constant> + '_ {
485        self.items.iter_mut().filter_map(|item| match item {
486            Export::Constant(item) => Some(item),
487            _ => None,
488        })
489    }
490
491    /// Get an iterator over the types defined in this module.
492    pub fn types(&self) -> impl Iterator<Item = &TypeDecl> + '_ {
493        self.items.iter().filter_map(|item| match item {
494            Export::Type(item) => Some(item),
495            _ => None,
496        })
497    }
498
499    /// Same as [Module::types], but returns mutable references.
500    pub fn types_mut(&mut self) -> impl Iterator<Item = &mut TypeDecl> + '_ {
501        self.items.iter_mut().filter_map(|item| match item {
502            Export::Type(item) => Some(item),
503            _ => None,
504        })
505    }
506
507    /// Get an iterator over the procedures defined in this module.
508    pub fn procedures(&self) -> impl Iterator<Item = &Procedure> + '_ {
509        self.items.iter().filter_map(|item| match item {
510            Export::Procedure(item) => Some(item),
511            _ => None,
512        })
513    }
514
515    /// Same as [Module::procedures], but returns mutable references.
516    pub fn procedures_mut(&mut self) -> impl Iterator<Item = &mut Procedure> + '_ {
517        self.items.iter_mut().filter_map(|item| match item {
518            Export::Procedure(item) => Some(item),
519            _ => None,
520        })
521    }
522
523    /// Get an iterator over the item aliases in this module.
524    pub fn aliases(&self) -> impl Iterator<Item = &Alias> + '_ {
525        self.items.iter().filter_map(|item| match item {
526            Export::Alias(item) => Some(item),
527            _ => None,
528        })
529    }
530
531    /// Same as [Module::aliases], but returns mutable references.
532    pub fn aliases_mut(&mut self) -> impl Iterator<Item = &mut Alias> + '_ {
533        self.items.iter_mut().filter_map(|item| match item {
534            Export::Alias(item) => Some(item),
535            _ => None,
536        })
537    }
538
539    /// Get a reference to the items stored in this module
540    pub fn items(&self) -> &[Export] {
541        &self.items
542    }
543
544    /// Get a mutable reference to the storage for items defined in this module
545    pub fn items_mut(&mut self) -> &mut Vec<Export> {
546        &mut self.items
547    }
548
549    /// Returns items exported from this module.
550    ///
551    /// Each exported item is represented by its local item index and a fully qualified name.
552    pub fn exported(&self) -> impl Iterator<Item = (ItemIndex, QualifiedProcedureName)> + '_ {
553        self.items.iter().enumerate().filter_map(|(idx, item)| {
554            // skip un-exported items
555            if !item.visibility().is_public() {
556                return None;
557            }
558
559            let idx = ItemIndex::new(idx);
560            let name = ProcedureName::from_raw_parts(item.name().clone());
561            let fqn = QualifiedProcedureName::new(self.path.clone(), name);
562
563            Some((idx, fqn))
564        })
565    }
566
567    /// Gets the type signature for the given [ItemIndex], if available.
568    pub fn procedure_signature(&self, id: ItemIndex) -> Option<&FunctionType> {
569        self.items[id.as_usize()].signature()
570    }
571
572    /// Get the item at `index` in this module's item table.
573    ///
574    /// The item returned may be either a locally-defined item, or a re-exported item. See [Export]
575    /// for details.
576    pub fn get(&self, index: ItemIndex) -> Option<&Export> {
577        self.items.get(index.as_usize())
578    }
579
580    /// Get the [ItemIndex] for the first item in this module's item table which returns true for
581    /// `predicate`.
582    pub fn index_of<F>(&self, predicate: F) -> Option<ItemIndex>
583    where
584        F: FnMut(&Export) -> bool,
585    {
586        self.items.iter().position(predicate).map(ItemIndex::new)
587    }
588
589    /// Get the [ItemIndex] for the item whose name is `name` in this module's item table, _if_ that
590    /// item is exported.
591    ///
592    /// Non-exported items can be retrieved by using [Module::index_of].
593    pub fn index_of_name(&self, name: &Ident) -> Option<ItemIndex> {
594        self.index_of(|item| item.name() == name && item.visibility().is_public())
595    }
596
597    /// Resolves `name` to an item within the local scope of this module
598    pub fn resolve(
599        &self,
600        name: Span<&str>,
601        source_manager: Arc<dyn SourceManager>,
602    ) -> Result<SymbolResolution, SymbolResolutionError> {
603        let resolver = self.resolver(source_manager)?;
604        resolver.resolve(name)
605    }
606
607    /// Resolves `path` to an item within the local scope of this module
608    pub fn resolve_path(
609        &self,
610        path: Span<&Path>,
611        source_manager: Arc<dyn SourceManager>,
612    ) -> Result<SymbolResolution, SymbolResolutionError> {
613        let resolver = self.resolver(source_manager)?;
614        resolver.resolve_path(path)
615    }
616
617    /// Construct a search structure that can resolve procedure names local to this module
618    #[inline]
619    pub fn resolver(
620        &self,
621        source_manager: Arc<dyn SourceManager>,
622    ) -> Result<LocalSymbolResolver, SymbolResolutionError> {
623        LocalSymbolResolver::new(self, source_manager)
624    }
625
626    /// Resolves `module_name` to an [Alias] within the context of this module
627    pub fn get_import(&self, module_name: &str) -> Option<&Alias> {
628        self.items.iter().find_map(|item| match item {
629            Export::Alias(item) if item.name().as_str() == module_name => Some(item),
630            _ => None,
631        })
632    }
633
634    /// Same as [Module::get_import], but returns a mutable reference to the [Alias]
635    pub fn get_import_mut(&mut self, module_name: &str) -> Option<&mut Alias> {
636        self.items.iter_mut().find_map(|item| match item {
637            Export::Alias(item) if item.name().as_str() == module_name => Some(item),
638            _ => None,
639        })
640    }
641
642    /// Resolves a user-expressed type, `ty`, to a concrete type
643    pub fn resolve_type(
644        &self,
645        ty: &ast::TypeExpr,
646        source_manager: Arc<dyn SourceManager>,
647    ) -> Result<Option<types::Type>, SymbolResolutionError> {
648        let mut type_resolver = self.type_resolver(source_manager)?;
649        type_resolver.resolve(ty)
650    }
651
652    /// Get a type resolver for this module
653    pub fn type_resolver(
654        &self,
655        source_manager: Arc<dyn SourceManager>,
656    ) -> Result<impl TypeResolver<SymbolResolutionError> + '_, SymbolResolutionError> {
657        ModuleTypeResolver::new(self, source_manager)
658    }
659}
660
661impl core::ops::Index<ItemIndex> for Module {
662    type Output = Export;
663
664    #[inline]
665    fn index(&self, index: ItemIndex) -> &Self::Output {
666        &self.items[index.as_usize()]
667    }
668}
669
670impl core::ops::IndexMut<ItemIndex> for Module {
671    #[inline]
672    fn index_mut(&mut self, index: ItemIndex) -> &mut Self::Output {
673        &mut self.items[index.as_usize()]
674    }
675}
676
677impl Spanned for Module {
678    fn span(&self) -> SourceSpan {
679        self.span
680    }
681}
682
683impl Eq for Module {}
684
685impl PartialEq for Module {
686    fn eq(&self, other: &Self) -> bool {
687        self.kind == other.kind
688            && self.path == other.path
689            && self.docs == other.docs
690            && self.items == other.items
691    }
692}
693
694/// Debug representation of this module
695impl fmt::Debug for Module {
696    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697        f.debug_struct("Module")
698            .field("docs", &self.docs)
699            .field("path", &self.path)
700            .field("kind", &self.kind)
701            .field("items", &self.items)
702            .finish()
703    }
704}
705
706/// Pretty-printed representation of this module as Miden Assembly text format
707///
708/// NOTE: Delegates to the [crate::prettier::PrettyPrint] implementation internally
709impl fmt::Display for Module {
710    /// Writes this [Module] as formatted MASM code into the formatter.
711    ///
712    /// The formatted code puts each instruction on a separate line and preserves correct
713    /// indentation for instruction blocks.
714    #[inline]
715    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716        use crate::prettier::PrettyPrint;
717
718        self.pretty_print(f)
719    }
720}
721
722/// The pretty-printer for [Module]
723impl crate::prettier::PrettyPrint for Module {
724    fn render(&self) -> crate::prettier::Document {
725        use crate::prettier::*;
726
727        let mut doc = self
728            .docs
729            .as_ref()
730            .map(|docstring| docstring.render() + nl())
731            .unwrap_or(Document::Empty);
732
733        for (item_index, item) in self.items.iter().enumerate() {
734            if item_index > 0 {
735                doc += nl();
736            }
737            doc += item.render();
738        }
739
740        doc
741    }
742}
743
744struct ModuleTypeResolver<'a> {
745    module: &'a Module,
746    resolver: LocalSymbolResolver,
747}
748
749impl<'a> ModuleTypeResolver<'a> {
750    pub fn new(
751        module: &'a Module,
752        source_manager: Arc<dyn SourceManager>,
753    ) -> Result<Self, SymbolResolutionError> {
754        let resolver = module.resolver(source_manager)?;
755        Ok(Self { module, resolver })
756    }
757}
758
759impl TypeResolver<SymbolResolutionError> for ModuleTypeResolver<'_> {
760    fn source_manager(&self) -> Arc<dyn SourceManager> {
761        self.resolver.source_manager()
762    }
763    fn get_type(
764        &mut self,
765        context: SourceSpan,
766        _gid: GlobalItemIndex,
767    ) -> Result<types::Type, SymbolResolutionError> {
768        Err(SymbolResolutionError::undefined(context, &self.resolver.source_manager()))
769    }
770    fn get_local_type(
771        &mut self,
772        context: SourceSpan,
773        id: ItemIndex,
774    ) -> Result<Option<types::Type>, SymbolResolutionError> {
775        match &self.module[id] {
776            Export::Type(ty) => match ty {
777                TypeDecl::Alias(ty) => self.resolve(&ty.ty),
778                TypeDecl::Enum(ty) => Ok(Some(ty.ty().clone())),
779            },
780            item => Err(self.resolve_local_failed(SymbolResolutionError::invalid_symbol_type(
781                context,
782                "type",
783                item.span(),
784                &self.resolver.source_manager(),
785            ))),
786        }
787    }
788    #[inline(always)]
789    fn resolve_local_failed(&self, err: SymbolResolutionError) -> SymbolResolutionError {
790        err
791    }
792    fn resolve_type_ref(
793        &mut self,
794        path: Span<&Path>,
795    ) -> Result<SymbolResolution, SymbolResolutionError> {
796        self.resolver.resolve_path(path)
797    }
798}