sway_core/semantic_analysis/namespace/
namespace.rs

1use crate::{language::Visibility, Engines, Ident};
2
3use super::{module::Module, root::Root, ModulePath, ModulePathBuf};
4
5use sway_error::handler::{ErrorEmitted, Handler};
6use sway_types::{
7    constants::{CONTRACT_ID, CORE, PRELUDE, STD},
8    span::Span,
9};
10
11/// The set of items that represent the namespace context passed throughout type checking.
12#[derive(Clone, Debug)]
13pub struct Namespace {
14    /// The `root` of the project namespace.
15    ///
16    /// From the root, the entirety of the project's namespace can always be accessed.
17    ///
18    /// The root is initialised from the `init` namespace before type-checking begins.
19    pub(crate) root: Root,
20    /// An absolute path from the `root` that represents the module location.
21    ///
22    /// The path of the root module in a package is `[package_name]`. If a module `X` is a submodule
23    /// of module `Y` which is a submodule of the root module in the package `P`, then the path is
24    /// `[P, Y, X]`.
25    pub(crate) current_mod_path: ModulePathBuf,
26}
27
28impl Namespace {
29    /// Initialize the namespace
30    /// See also the factory functions in contract_helpers.rs
31    ///
32    /// If `import_preludes_into_root` is true then core::prelude::* and std::prelude::* will be
33    /// imported into the root module, provided core and std are available in the external modules.
34    pub fn new(
35        handler: &Handler,
36        engines: &Engines,
37        package_root: Root,
38        import_preludes_into_root: bool,
39    ) -> Result<Self, ErrorEmitted> {
40        let package_name = package_root.current_package_name().clone();
41        let mut res = Self {
42            root: package_root,
43            current_mod_path: vec![package_name],
44        };
45
46        if import_preludes_into_root {
47            res.import_implicits(handler, engines)?;
48        }
49        Ok(res)
50    }
51
52    pub fn root(self) -> Root {
53        self.root
54    }
55
56    pub fn root_ref(&self) -> &Root {
57        &self.root
58    }
59
60    pub fn current_module(&self) -> &Module {
61        self.root
62            .module_in_current_package(&self.current_mod_path)
63            .unwrap_or_else(|| panic!("Could not retrieve submodule for mod_path."))
64    }
65
66    pub fn current_module_mut(&mut self) -> &mut Module {
67        self.root
68            .module_mut_in_current_package(&self.current_mod_path)
69            .unwrap_or_else(|| panic!("Could not retrieve submodule for mod_path."))
70    }
71
72    pub(crate) fn current_module_has_submodule(&self, submod_name: &Ident) -> bool {
73        self.current_module()
74            .submodule(&[submod_name.clone()])
75            .is_some()
76    }
77
78    pub fn current_package_name(&self) -> &Ident {
79        self.root.current_package_name()
80    }
81
82    /// A reference to the path of the module currently being processed.
83    pub fn current_mod_path(&self) -> &ModulePathBuf {
84        &self.current_mod_path
85    }
86
87    /// Prepends the module path into the prefixes.
88    pub fn prepend_module_path<'a>(
89        &'a self,
90        prefixes: impl IntoIterator<Item = &'a Ident>,
91    ) -> ModulePathBuf {
92        self.current_mod_path
93            .iter()
94            .chain(prefixes)
95            .cloned()
96            .collect()
97    }
98
99    /// Convert a parsed path to a full path.
100    pub fn parsed_path_to_full_path(
101        &self,
102        _engines: &Engines,
103        parsed_path: &ModulePathBuf,
104        is_relative_to_package_root: bool,
105    ) -> ModulePathBuf {
106        if is_relative_to_package_root {
107            // Path is relative to the root module in the current package. Prepend the package name
108            let mut path = vec![self.current_package_name().clone()];
109            for ident in parsed_path.iter() {
110                path.push(ident.clone())
111            }
112            path
113        } else if self.current_module_has_submodule(&parsed_path[0]) {
114            // The first identifier is a submodule of the current module
115            // The path is therefore assumed to be relative to the current module, so prepend the current module path.
116            self.prepend_module_path(parsed_path)
117        } else if self.module_is_external(parsed_path) {
118            // The path refers to an external module, so the path is already a full path.
119            parsed_path.to_vec()
120        } else {
121            // The first identifier is neither a submodule nor an external package. It must
122            // therefore refer to a binding in the local environment
123            self.prepend_module_path(parsed_path)
124        }
125    }
126
127    pub fn current_package_root_module(&self) -> &Module {
128        self.root.current_package_root_module()
129    }
130
131    pub fn module_from_absolute_path(&self, path: &ModulePathBuf) -> Option<&Module> {
132        self.root.module_from_absolute_path(path)
133    }
134
135    // Like module_from_absolute_path, but throws an error if the module is not found
136    pub fn require_module_from_absolute_path(
137        &self,
138        handler: &Handler,
139        path: &ModulePathBuf,
140    ) -> Result<&Module, ErrorEmitted> {
141        self.root.require_module(handler, path)
142    }
143
144    /// Returns true if the current module being checked is a direct or indirect submodule of
145    /// the module given by the `absolute_module_path`.
146    ///
147    /// The current module being checked is determined by `mod_path`.
148    ///
149    /// E.g., the `mod_path` `[fist, second, third]` of the root `foo` is a submodule of the module
150    /// `[foo, first]`. Note that the `mod_path` does not contain the root name, while the
151    /// `absolute_module_path` always contains it.
152    ///
153    /// If the current module being checked is the same as the module given by the `absolute_module_path`,
154    /// the `true_if_same` is returned.
155    pub(crate) fn module_is_submodule_of(
156        &self,
157        absolute_module_path: &ModulePath,
158        true_if_same: bool,
159    ) -> bool {
160        if self.current_mod_path.len() < absolute_module_path.len() {
161            return false;
162        }
163
164        let is_submodule = absolute_module_path
165            .iter()
166            .zip(self.current_mod_path.iter())
167            .all(|(left, right)| left == right);
168
169        if is_submodule {
170            if self.current_mod_path.len() == absolute_module_path.len() {
171                true_if_same
172            } else {
173                true
174            }
175        } else {
176            false
177        }
178    }
179
180    /// Returns true if the module given by the `absolute_module_path` is external
181    /// to the current package. External modules are imported in the `Forc.toml` file.
182    pub(crate) fn module_is_external(&self, absolute_module_path: &ModulePath) -> bool {
183        assert!(!absolute_module_path.is_empty(), "Absolute module path must have at least one element, because it always contains the package name.");
184
185        self.root.current_package_name() != &absolute_module_path[0]
186    }
187
188    pub fn package_exists(&self, name: &Ident) -> bool {
189        self.module_from_absolute_path(&vec![name.clone()])
190            .is_some()
191    }
192
193    pub(crate) fn module_has_binding(
194        &self,
195        engines: &Engines,
196        mod_path: &ModulePathBuf,
197        symbol: &Ident,
198    ) -> bool {
199        let dummy_handler = Handler::default();
200        if let Some(module) = self.module_from_absolute_path(mod_path) {
201            module
202                .resolve_symbol(&dummy_handler, engines, symbol)
203                .is_ok()
204        } else {
205            false
206        }
207    }
208
209    // Import core::prelude::*, std::prelude::* and ::CONTRACT_ID as appropriate into the current module
210    fn import_implicits(
211        &mut self,
212        handler: &Handler,
213        engines: &Engines,
214    ) -> Result<(), ErrorEmitted> {
215        // Import preludes
216        let package_name = self.current_package_name().to_string();
217        let core_string = CORE.to_string();
218        let core_ident = Ident::new_no_span(core_string.clone());
219        let prelude_ident = Ident::new_no_span(PRELUDE.to_string());
220        if package_name == CORE {
221            // Do nothing
222        } else if package_name == STD {
223            // Import core::prelude::*
224            assert!(self.root.exists_as_external(&core_string));
225            self.root.star_import(
226                handler,
227                engines,
228                &[core_ident, prelude_ident],
229                &self.current_mod_path,
230                Visibility::Private,
231            )?
232        } else {
233            // Import core::prelude::* and std::prelude::*
234            if self.root.exists_as_external(&core_string) {
235                self.root.star_import(
236                    handler,
237                    engines,
238                    &[core_ident, prelude_ident.clone()],
239                    &self.current_mod_path,
240                    Visibility::Private,
241                )?;
242            }
243
244            let std_string = STD.to_string();
245            // Only import std::prelude::* if std exists as a dependency
246            if self.root.exists_as_external(&std_string) {
247                self.root.star_import(
248                    handler,
249                    engines,
250                    &[Ident::new_no_span(std_string), prelude_ident],
251                    &self.current_mod_path,
252                    Visibility::Private,
253                )?
254            }
255        }
256
257        // Import contract id. CONTRACT_ID is declared in the root module, so only import it into non-root modules
258        if self.root.is_contract_package() && self.current_mod_path.len() > 1 {
259            // import ::CONTRACT_ID
260            self.root.item_import(
261                handler,
262                engines,
263                &[Ident::new_no_span(package_name)],
264                &Ident::new_no_span(CONTRACT_ID.to_string()),
265                &self.current_mod_path,
266                None,
267                Visibility::Private,
268            )?
269        }
270
271        Ok(())
272    }
273
274    pub(crate) fn enter_submodule(
275        &mut self,
276        handler: &Handler,
277        engines: &Engines,
278        mod_name: Ident,
279        visibility: Visibility,
280        module_span: Span,
281    ) -> Result<(), ErrorEmitted> {
282        let mut import_implicits = false;
283
284        // Ensure the new module exists and is initialized properly
285        if !self
286            .current_module()
287            .submodules()
288            .contains_key(&mod_name.to_string())
289        {
290            // Entering a new module. Add a new one.
291            self.current_module_mut()
292                .add_new_submodule(&mod_name, visibility, Some(module_span));
293            import_implicits = true;
294        }
295
296        // Update self to point to the new module
297        self.current_mod_path.push(mod_name.clone());
298
299        // Import implicits into the newly created module.
300        if import_implicits {
301            self.import_implicits(handler, engines)?;
302        }
303
304        Ok(())
305    }
306
307    /// Pushes a new submodule to the namespace's module hierarchy.
308    pub fn push_submodule(
309        &mut self,
310        handler: &Handler,
311        engines: &Engines,
312        mod_name: Ident,
313        visibility: Visibility,
314        module_span: Span,
315    ) -> Result<(), ErrorEmitted> {
316        match self.enter_submodule(handler, engines, mod_name, visibility, module_span) {
317            Ok(_) => Ok(()),
318            Err(e) => Err(e),
319        }
320    }
321
322    /// Pops the current submodule from the namespace's module hierarchy.
323    pub fn pop_submodule(&mut self) {
324        self.current_mod_path.pop();
325    }
326
327    pub(crate) fn star_import_to_current_module(
328        &mut self,
329        handler: &Handler,
330        engines: &Engines,
331        src: &ModulePath,
332        visibility: Visibility,
333    ) -> Result<(), ErrorEmitted> {
334        self.root
335            .star_import(handler, engines, src, &self.current_mod_path, visibility)
336    }
337
338    pub(crate) fn variant_star_import_to_current_module(
339        &mut self,
340        handler: &Handler,
341        engines: &Engines,
342        src: &ModulePath,
343        enum_name: &Ident,
344        visibility: Visibility,
345    ) -> Result<(), ErrorEmitted> {
346        self.root.variant_star_import(
347            handler,
348            engines,
349            src,
350            &self.current_mod_path,
351            enum_name,
352            visibility,
353        )
354    }
355
356    pub(crate) fn self_import_to_current_module(
357        &mut self,
358        handler: &Handler,
359        engines: &Engines,
360        src: &ModulePath,
361        alias: Option<Ident>,
362        visibility: Visibility,
363    ) -> Result<(), ErrorEmitted> {
364        self.root.self_import(
365            handler,
366            engines,
367            src,
368            &self.current_mod_path,
369            alias,
370            visibility,
371        )
372    }
373
374    pub(crate) fn item_import_to_current_module(
375        &mut self,
376        handler: &Handler,
377        engines: &Engines,
378        src: &ModulePath,
379        item: &Ident,
380        alias: Option<Ident>,
381        visibility: Visibility,
382    ) -> Result<(), ErrorEmitted> {
383        self.root.item_import(
384            handler,
385            engines,
386            src,
387            item,
388            &self.current_mod_path,
389            alias,
390            visibility,
391        )
392    }
393
394    #[allow(clippy::too_many_arguments)]
395    pub(crate) fn variant_import_to_current_module(
396        &mut self,
397        handler: &Handler,
398        engines: &Engines,
399        src: &ModulePath,
400        enum_name: &Ident,
401        variant_name: &Ident,
402        alias: Option<Ident>,
403        visibility: Visibility,
404    ) -> Result<(), ErrorEmitted> {
405        self.root.variant_import(
406            handler,
407            engines,
408            src,
409            enum_name,
410            variant_name,
411            &self.current_mod_path,
412            alias,
413            visibility,
414        )
415    }
416}