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
123            .docs
124            .as_ref()
125            .map(|docstring| docstring.render())
126            .unwrap_or(Document::Empty);
127
128        if self.visibility.is_public() {
129            doc += display(self.visibility) + const_text(" ");
130        }
131        doc += const_text("use ");
132        doc += match &self.target {
133            target @ AliasTarget::MastRoot(_) => display(format_args!("{}->{}", target, self.name)),
134            target => {
135                let prefix = if self.is_absolute() { "::" } else { "" };
136                if self.is_renamed() {
137                    display(format_args!("{}{}->{}", prefix, target, &self.name))
138                } else {
139                    display(format_args!("{prefix}{target}"))
140                }
141            },
142        };
143        doc
144    }
145}
146
147/// A fully-qualified external item that is the target of an alias
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub enum AliasTarget {
150    /// An alias of the procedure whose root is the given digest
151    ///
152    /// This target type is only valid for procedures.
153    ///
154    /// Corresponds to [`InvocationTarget::MastRoot`]
155    MastRoot(Span<Word>),
156    /// An alias of an item using a path that may need to be resolved relative to the importing
157    /// module.
158    ///
159    /// Corresponds to [`InvocationTarget::Path`]
160    Path(Span<Arc<Path>>),
161}
162
163impl AliasTarget {
164    pub fn unwrap_path(&self) -> &Arc<Path> {
165        match self {
166            Self::Path(path) => path.inner(),
167            Self::MastRoot(_) => {
168                panic!("expected alias target to be a path, but got a mast digest")
169            },
170        }
171    }
172}
173
174impl Spanned for AliasTarget {
175    fn span(&self) -> SourceSpan {
176        match self {
177            Self::MastRoot(spanned) => spanned.span(),
178            Self::Path(spanned) => spanned.span(),
179        }
180    }
181}
182
183impl From<Span<Word>> for AliasTarget {
184    fn from(digest: Span<Word>) -> Self {
185        Self::MastRoot(digest)
186    }
187}
188
189impl TryFrom<InvocationTarget> for AliasTarget {
190    type Error = InvocationTarget;
191
192    fn try_from(target: InvocationTarget) -> Result<Self, Self::Error> {
193        match target {
194            InvocationTarget::MastRoot(digest) => Ok(Self::MastRoot(digest)),
195            InvocationTarget::Path(path) => Ok(Self::Path(path)),
196            target @ InvocationTarget::Symbol(_) => Err(target),
197        }
198    }
199}
200
201impl From<&AliasTarget> for InvocationTarget {
202    fn from(target: &AliasTarget) -> Self {
203        match target {
204            AliasTarget::MastRoot(digest) => Self::MastRoot(*digest),
205            AliasTarget::Path(path) => Self::Path(path.clone()),
206        }
207    }
208}
209impl From<AliasTarget> for InvocationTarget {
210    fn from(target: AliasTarget) -> Self {
211        match target {
212            AliasTarget::MastRoot(digest) => Self::MastRoot(digest),
213            AliasTarget::Path(path) => Self::Path(path),
214        }
215    }
216}
217
218impl crate::prettier::PrettyPrint for AliasTarget {
219    fn render(&self) -> crate::prettier::Document {
220        use miden_core::utils::DisplayHex;
221
222        use crate::prettier::*;
223
224        match self {
225            Self::MastRoot(digest) => display(DisplayHex(digest.as_bytes().as_slice())),
226            Self::Path(path) => display(path),
227        }
228    }
229}
230
231impl fmt::Display for AliasTarget {
232    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233        use crate::prettier::PrettyPrint;
234
235        self.pretty_print(f)
236    }
237}