Skip to main content

miden_assembly_syntax/ast/
alias.rs

1use alloc::{string::String, sync::Arc};
2use core::fmt;
3
4use miden_debug_types::{SourceSpan, Span, Spanned};
5
6use crate::{
7    Path, Word,
8    ast::{DocString, Ident, InvocationTarget, Visibility},
9};
10
11// ITEM ALIAS
12// ================================================================================================
13
14/// Represents an item that acts like it is locally-defined, but is actually externally-defined.
15///
16/// These "aliases" do not have a concrete representation in the module, but are instead resolved
17/// during compilation to refer directly to the aliased item.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Alias {
20    /// The documentation attached to this item
21    docs: Option<DocString>,
22    /// The visibility of this item
23    visibility: Visibility,
24    /// The name of this item
25    name: Ident,
26    /// The underlying item being aliased.
27    ///
28    /// Alias targets are context-sensitive, depending on how they were defined and what stage of
29    /// compilation we're in. See [AliasTarget] for semantics of each target type, but they closely
30    /// correspond to [InvocationTarget].
31    ///
32    /// NOTE: `AliasTarget::MastRoot` is _only_ a valid target for procedure items
33    target: AliasTarget,
34    /// The number of times this import has been used locally.
35    pub uses: usize,
36}
37
38impl Alias {
39    /// Creates a new item alias called `name`, which resolves to `target`.
40    pub fn new(visibility: Visibility, name: Ident, target: AliasTarget) -> Self {
41        Self {
42            docs: None,
43            visibility,
44            name,
45            target,
46            uses: 0,
47        }
48    }
49
50    /// Adds documentation to this alias.
51    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
52        self.docs = docs.map(DocString::new);
53        self
54    }
55
56    /// Returns the documentation associated with this item.
57    pub fn docs(&self) -> Option<Span<&str>> {
58        self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
59    }
60
61    /// Returns the visibility of this alias
62    #[inline]
63    pub const fn visibility(&self) -> Visibility {
64        self.visibility
65    }
66
67    /// Returns the name of this alias within its containing module.
68    ///
69    /// If the item is simply re-exported with the same name, this will be equivalent to
70    /// `self.target().name`
71    #[inline]
72    pub fn name(&self) -> &Ident {
73        &self.name
74    }
75
76    /// Returns the target of this alias
77    #[inline]
78    pub fn target(&self) -> &AliasTarget {
79        &self.target
80    }
81
82    /// Returns a mutable reference to the target of this alias
83    #[inline]
84    pub fn target_mut(&mut self) -> &mut AliasTarget {
85        &mut self.target
86    }
87
88    /// Returns true if this alias uses an absolute target path
89    #[inline]
90    pub fn is_absolute(&self) -> bool {
91        match self.target() {
92            AliasTarget::MastRoot(_) => true,
93            AliasTarget::Path(path) => path.is_absolute(),
94        }
95    }
96
97    /// Returns true if this alias uses a different name than the target
98    #[inline]
99    pub fn is_renamed(&self) -> bool {
100        match self.target() {
101            AliasTarget::MastRoot(_) => true,
102            AliasTarget::Path(path) => path.last().unwrap() != self.name.as_str(),
103        }
104    }
105
106    /// Returns true if this import has at least one use in its containing module.
107    pub fn is_used(&self) -> bool {
108        self.uses > 0 || self.visibility.is_public()
109    }
110}
111
112impl Spanned for Alias {
113    fn span(&self) -> SourceSpan {
114        self.target.span()
115    }
116}
117
118impl crate::prettier::PrettyPrint for Alias {
119    fn render(&self) -> crate::prettier::Document {
120        use crate::prettier::*;
121
122        let mut doc = self.docs.as_ref().map(PrettyPrint::render).unwrap_or(Document::Empty);
123
124        if self.visibility.is_public() {
125            doc += display(self.visibility) + const_text(" ");
126        }
127        doc += const_text("use ");
128        doc += match &self.target {
129            target @ AliasTarget::MastRoot(_) => display(format_args!("{}->{}", target, self.name)),
130            target => {
131                let prefix = if self.is_absolute() { "::" } else { "" };
132                if self.is_renamed() {
133                    display(format_args!("{}{}->{}", prefix, target, &self.name))
134                } else {
135                    display(format_args!("{prefix}{target}"))
136                }
137            },
138        };
139        doc
140    }
141}
142
143/// A fully-qualified external item that is the target of an alias
144#[derive(Debug, Clone, PartialEq, Eq)]
145pub enum AliasTarget {
146    /// An alias of the procedure whose root is the given digest
147    ///
148    /// This target type is only valid for procedures.
149    ///
150    /// Corresponds to [`InvocationTarget::MastRoot`]
151    MastRoot(Span<Word>),
152    /// An alias of an item using a path that may need to be resolved relative to the importing
153    /// module.
154    ///
155    /// Corresponds to [`InvocationTarget::Path`]
156    Path(Span<Arc<Path>>),
157}
158
159impl AliasTarget {
160    pub fn unwrap_path(&self) -> &Arc<Path> {
161        match self {
162            Self::Path(path) => path.inner(),
163            Self::MastRoot(_) => {
164                panic!("expected alias target to be a path, but got a mast digest")
165            },
166        }
167    }
168}
169
170impl Spanned for AliasTarget {
171    fn span(&self) -> SourceSpan {
172        match self {
173            Self::MastRoot(spanned) => spanned.span(),
174            Self::Path(spanned) => spanned.span(),
175        }
176    }
177}
178
179impl From<Span<Word>> for AliasTarget {
180    fn from(digest: Span<Word>) -> Self {
181        Self::MastRoot(digest)
182    }
183}
184
185impl TryFrom<InvocationTarget> for AliasTarget {
186    type Error = InvocationTarget;
187
188    fn try_from(target: InvocationTarget) -> Result<Self, Self::Error> {
189        match target {
190            InvocationTarget::MastRoot(digest) => Ok(Self::MastRoot(digest)),
191            InvocationTarget::Path(path) => Ok(Self::Path(path)),
192            target @ InvocationTarget::Symbol(_) => Err(target),
193        }
194    }
195}
196
197impl From<&AliasTarget> for InvocationTarget {
198    fn from(target: &AliasTarget) -> Self {
199        match target {
200            AliasTarget::MastRoot(digest) => Self::MastRoot(*digest),
201            AliasTarget::Path(path) => Self::Path(path.clone()),
202        }
203    }
204}
205impl From<AliasTarget> for InvocationTarget {
206    fn from(target: AliasTarget) -> Self {
207        match target {
208            AliasTarget::MastRoot(digest) => Self::MastRoot(digest),
209            AliasTarget::Path(path) => Self::Path(path),
210        }
211    }
212}
213
214impl crate::prettier::PrettyPrint for AliasTarget {
215    fn render(&self) -> crate::prettier::Document {
216        use miden_core::utils::DisplayHex;
217
218        use crate::prettier::*;
219
220        match self {
221            Self::MastRoot(digest) => display(DisplayHex(digest.as_bytes().as_slice())),
222            Self::Path(path) => display(path),
223        }
224    }
225}
226
227impl fmt::Display for AliasTarget {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        use crate::prettier::PrettyPrint;
230
231        self.pretty_print(f)
232    }
233}