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, 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_prelude_into_root` is true then and std::prelude::* will be
33    /// imported into the root module, provided std is available in the external modules.
34    pub fn new(
35        handler: &Handler,
36        engines: &Engines,
37        package_root: Root,
38        import_prelude_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_prelude_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 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 prelude_ident = Ident::new_no_span(PRELUDE.to_string());
218
219        if package_name == STD {
220            // Do nothing
221        } else {
222            // Import std::prelude::*
223            let std_string = STD.to_string();
224            // Only import std::prelude::* if std exists as a dependency
225            if self.root.exists_as_external(&std_string) {
226                self.root.star_import(
227                    handler,
228                    engines,
229                    &[Ident::new_no_span(std_string), prelude_ident],
230                    &self.current_mod_path,
231                    Visibility::Private,
232                )?
233            }
234        }
235
236        // Import contract id. CONTRACT_ID is declared in the root module, so only import it into non-root modules
237        if self.root.is_contract_package() && self.current_mod_path.len() > 1 {
238            // import ::CONTRACT_ID
239            self.root.item_import(
240                handler,
241                engines,
242                &[Ident::new_no_span(package_name)],
243                &Ident::new_no_span(CONTRACT_ID.to_string()),
244                &self.current_mod_path,
245                None,
246                Visibility::Private,
247            )?
248        }
249
250        Ok(())
251    }
252
253    pub(crate) fn enter_submodule(
254        &mut self,
255        handler: &Handler,
256        engines: &Engines,
257        mod_name: Ident,
258        visibility: Visibility,
259        module_span: Span,
260    ) -> Result<(), ErrorEmitted> {
261        let mut import_implicits = false;
262
263        // Ensure the new module exists and is initialized properly
264        if !self
265            .current_module()
266            .submodules()
267            .contains_key(&mod_name.to_string())
268        {
269            // Entering a new module. Add a new one.
270            self.current_module_mut()
271                .add_new_submodule(&mod_name, visibility, Some(module_span));
272            import_implicits = true;
273        }
274
275        // Update self to point to the new module
276        self.current_mod_path.push(mod_name.clone());
277
278        // Import implicits into the newly created module.
279        if import_implicits {
280            self.import_implicits(handler, engines)?;
281        }
282
283        Ok(())
284    }
285
286    /// Pushes a new submodule to the namespace's module hierarchy.
287    pub fn push_submodule(
288        &mut self,
289        handler: &Handler,
290        engines: &Engines,
291        mod_name: Ident,
292        visibility: Visibility,
293        module_span: Span,
294    ) -> Result<(), ErrorEmitted> {
295        match self.enter_submodule(handler, engines, mod_name, visibility, module_span) {
296            Ok(_) => Ok(()),
297            Err(e) => Err(e),
298        }
299    }
300
301    /// Pops the current submodule from the namespace's module hierarchy.
302    pub fn pop_submodule(&mut self) {
303        self.current_mod_path.pop();
304    }
305
306    pub(crate) fn star_import_to_current_module(
307        &mut self,
308        handler: &Handler,
309        engines: &Engines,
310        src: &ModulePath,
311        visibility: Visibility,
312    ) -> Result<(), ErrorEmitted> {
313        self.root
314            .star_import(handler, engines, src, &self.current_mod_path, visibility)
315    }
316
317    pub(crate) fn variant_star_import_to_current_module(
318        &mut self,
319        handler: &Handler,
320        engines: &Engines,
321        src: &ModulePath,
322        enum_name: &Ident,
323        visibility: Visibility,
324    ) -> Result<(), ErrorEmitted> {
325        self.root.variant_star_import(
326            handler,
327            engines,
328            src,
329            &self.current_mod_path,
330            enum_name,
331            visibility,
332        )
333    }
334
335    pub(crate) fn self_import_to_current_module(
336        &mut self,
337        handler: &Handler,
338        engines: &Engines,
339        src: &ModulePath,
340        alias: Option<Ident>,
341        visibility: Visibility,
342    ) -> Result<(), ErrorEmitted> {
343        self.root.self_import(
344            handler,
345            engines,
346            src,
347            &self.current_mod_path,
348            alias,
349            visibility,
350        )
351    }
352
353    pub(crate) fn item_import_to_current_module(
354        &mut self,
355        handler: &Handler,
356        engines: &Engines,
357        src: &ModulePath,
358        item: &Ident,
359        alias: Option<Ident>,
360        visibility: Visibility,
361    ) -> Result<(), ErrorEmitted> {
362        self.root.item_import(
363            handler,
364            engines,
365            src,
366            item,
367            &self.current_mod_path,
368            alias,
369            visibility,
370        )
371    }
372
373    #[allow(clippy::too_many_arguments)]
374    pub(crate) fn variant_import_to_current_module(
375        &mut self,
376        handler: &Handler,
377        engines: &Engines,
378        src: &ModulePath,
379        enum_name: &Ident,
380        variant_name: &Ident,
381        alias: Option<Ident>,
382        visibility: Visibility,
383    ) -> Result<(), ErrorEmitted> {
384        self.root.variant_import(
385            handler,
386            engines,
387            src,
388            enum_name,
389            variant_name,
390            &self.current_mod_path,
391            alias,
392            visibility,
393        )
394    }
395}