sway_core/semantic_analysis/namespace/
namespace.rs

1use crate::{
2    decl_engine::DeclRef,
3    language::{parsed::*, Visibility},
4    ty::{self, TyDecl},
5    Engines, Ident,
6};
7
8use super::{
9    module::Module, package::Package, trait_map::TraitMap, ModuleName, ModulePath, ModulePathBuf,
10    ResolvedDeclaration,
11};
12
13use rustc_hash::FxHasher;
14use std::hash::BuildHasherDefault;
15
16use sway_error::{
17    error::CompileError,
18    handler::{ErrorEmitted, Handler},
19};
20use sway_types::{
21    constants::{CONTRACT_ID, PRELUDE, STD},
22    span::Span,
23    Spanned,
24};
25use sway_utils::iter_prefixes;
26
27/// The set of items that represent the namespace context passed throughout type checking.
28#[derive(Clone, Debug)]
29pub struct Namespace {
30    /// The current package, containing all the bindings found so far during compilation.
31    ///
32    /// The `Package` object should be supplied to `new` in order to be properly initialized. Note
33    /// also the existence of `contract_helpers::package_with_contract_id`.
34    pub(crate) current_package: Package,
35    /// An absolute path to the current module within the current package.
36    ///
37    /// The path of the root module in a package is `[package_name]`. If a module `X` is a submodule
38    /// of module `Y` which is a submodule of the root module in the package `P`, then the path is
39    /// `[P, Y, X]`.
40    pub(crate) current_mod_path: ModulePathBuf,
41}
42
43impl Namespace {
44    /// Initialize the namespace
45    /// See also `contract_helpers::package_with_contract_id`.
46    ///
47    /// If `import_std_prelude_into_root` is true then std::prelude::* will be imported into the
48    /// root module, provided std is available in the external modules.
49    pub fn new(
50        handler: &Handler,
51        engines: &Engines,
52        package: Package,
53        import_std_prelude_into_root: bool,
54    ) -> Result<Self, ErrorEmitted> {
55        let name = package.name().clone();
56        let mut res = Self {
57            current_package: package,
58            current_mod_path: vec![name],
59        };
60
61        if import_std_prelude_into_root {
62            res.import_implicits(handler, engines)?;
63        }
64        Ok(res)
65    }
66
67    pub fn current_package(self) -> Package {
68        self.current_package
69    }
70
71    pub fn current_package_ref(&self) -> &Package {
72        &self.current_package
73    }
74
75    fn module_in_current_package(&self, mod_path: &ModulePathBuf) -> Option<&Module> {
76        assert!(self.current_package.check_path_is_in_package(mod_path));
77        self.current_package.module_from_absolute_path(mod_path)
78    }
79
80    pub fn current_module(&self) -> &Module {
81        self.module_in_current_package(&self.current_mod_path)
82            .unwrap_or_else(|| {
83                panic!(
84                    "Could not retrieve submodule for mod_path: {:?}",
85                    self.current_mod_path
86                );
87            })
88    }
89
90    pub fn current_module_mut(&mut self) -> &mut Module {
91        let package_relative_path = Package::package_relative_path(&self.current_mod_path);
92        self.current_package
93            .root_module_mut()
94            .submodule_mut(package_relative_path)
95            .unwrap_or_else(|| {
96                panic!("Could not retrieve submodule for mod_path: {package_relative_path:?}");
97            })
98    }
99
100    pub(crate) fn current_module_has_submodule(&self, submod_name: &Ident) -> bool {
101        self.current_module()
102            .submodule(&[submod_name.clone()])
103            .is_some()
104    }
105
106    pub fn current_package_name(&self) -> &Ident {
107        self.current_package.name()
108    }
109
110    /// A reference to the path of the module currently being processed.
111    pub fn current_mod_path(&self) -> &ModulePathBuf {
112        &self.current_mod_path
113    }
114
115    /// Prepends the module path into the prefixes.
116    pub fn prepend_module_path<'a>(
117        &'a self,
118        prefixes: impl IntoIterator<Item = &'a Ident>,
119    ) -> ModulePathBuf {
120        self.current_mod_path
121            .iter()
122            .chain(prefixes)
123            .cloned()
124            .collect()
125    }
126
127    /// Convert a parsed path to a full path.
128    pub fn parsed_path_to_full_path(
129        &self,
130        _engines: &Engines,
131        parsed_path: &ModulePathBuf,
132        is_relative_to_package_root: bool,
133    ) -> ModulePathBuf {
134        if is_relative_to_package_root {
135            // Path is relative to the root module in the current package. Prepend the package name
136            let mut path = vec![self.current_package_name().clone()];
137            for ident in parsed_path.iter() {
138                path.push(ident.clone())
139            }
140            path
141        } else if self.current_module_has_submodule(&parsed_path[0]) {
142            // The first identifier is a submodule of the current module
143            // The path is therefore assumed to be relative to the current module, so prepend the current module path.
144            self.prepend_module_path(parsed_path)
145        } else if self.module_is_external(parsed_path) {
146            // The path refers to an external module, so the path is already a full path.
147            parsed_path.to_vec()
148        } else {
149            // The first identifier is neither a submodule nor an external package. It must
150            // therefore refer to a binding in the local environment
151            self.prepend_module_path(parsed_path)
152        }
153    }
154
155    pub fn current_package_root_module(&self) -> &Module {
156        self.current_package.root_module()
157    }
158
159    pub fn external_packages(
160        &self,
161    ) -> &im::HashMap<ModuleName, Package, BuildHasherDefault<FxHasher>> {
162        &self.current_package.external_packages
163    }
164
165    pub(crate) fn get_external_package(&self, package_name: &str) -> Option<&Package> {
166        self.current_package.external_packages.get(package_name)
167    }
168
169    pub(super) fn exists_as_external(&self, package_name: &str) -> bool {
170        self.get_external_package(package_name).is_some()
171    }
172
173    pub fn module_from_absolute_path(&self, path: &[Ident]) -> Option<&Module> {
174        if path.is_empty() {
175            None
176        } else {
177            self.current_package.module_from_absolute_path(path)
178        }
179    }
180
181    // Like module_from_absolute_path, but throws an error if the module is not found
182    pub fn require_module_from_absolute_path(
183        &self,
184        handler: &Handler,
185        path: &[Ident],
186    ) -> Result<&Module, ErrorEmitted> {
187        if path.is_empty() {
188            return Err(handler.emit_err(CompileError::Internal(
189                "Found empty absolute mod path",
190                Span::dummy(),
191            )));
192        }
193        let is_in_current_package = self.current_package.check_path_is_in_package(path);
194        match self.module_from_absolute_path(path) {
195            Some(module) => Ok(module),
196            None => Err(handler.emit_err(crate::namespace::module::module_not_found(
197                path,
198                is_in_current_package,
199            ))),
200        }
201    }
202
203    /// Returns true if the current module being checked is a direct or indirect submodule of
204    /// the module given by the `absolute_module_path`.
205    ///
206    /// The current module being checked is determined by `current_mod_path`.
207    ///
208    /// E.g., the mod_path `[fist, second, third]` of the root `foo` is a submodule of the module
209    /// `[foo, first]`.
210    ///
211    /// If the current module being checked is the same as the module given by the
212    /// `absolute_module_path`, the `true_if_same` is returned.
213    pub(crate) fn module_is_submodule_of(
214        &self,
215        absolute_module_path: &ModulePath,
216        true_if_same: bool,
217    ) -> bool {
218        if self.current_mod_path.len() < absolute_module_path.len() {
219            return false;
220        }
221
222        let is_submodule = absolute_module_path
223            .iter()
224            .zip(self.current_mod_path.iter())
225            .all(|(left, right)| left == right);
226
227        if is_submodule {
228            if self.current_mod_path.len() == absolute_module_path.len() {
229                true_if_same
230            } else {
231                true
232            }
233        } else {
234            false
235        }
236    }
237
238    /// Returns true if the module given by the `absolute_module_path` is external
239    /// to the current package. External modules are imported in the `Forc.toml` file.
240    pub(crate) fn module_is_external(&self, absolute_module_path: &ModulePath) -> bool {
241        assert!(!absolute_module_path.is_empty(), "Absolute module path must have at least one element, because it always contains the package name.");
242
243        self.current_package_name() != &absolute_module_path[0]
244    }
245
246    pub fn package_exists(&self, name: &Ident) -> bool {
247        self.module_from_absolute_path(&[name.clone()]).is_some()
248    }
249
250    pub(crate) fn module_has_binding(
251        &self,
252        engines: &Engines,
253        mod_path: &ModulePathBuf,
254        symbol: &Ident,
255    ) -> bool {
256        let dummy_handler = Handler::default();
257        if let Some(module) = self.module_from_absolute_path(mod_path) {
258            module
259                .resolve_symbol(&dummy_handler, engines, symbol)
260                .is_ok()
261        } else {
262            false
263        }
264    }
265
266    // Import std::prelude::* and ::CONTRACT_ID as appropriate into the current module
267    fn import_implicits(
268        &mut self,
269        handler: &Handler,
270        engines: &Engines,
271    ) -> Result<(), ErrorEmitted> {
272        // Import preludes
273        let package_name = self.current_package_name().to_string();
274        let prelude_ident = Ident::new_no_span(PRELUDE.to_string());
275
276        if package_name == STD {
277            // Do nothing
278        } else {
279            // Import std::prelude::*
280            let std_string = STD.to_string();
281            // Only import std::prelude::* if std exists as a dependency
282            if self.exists_as_external(&std_string) {
283                self.prelude_import(
284                    handler,
285                    engines,
286                    &[Ident::new_no_span(std_string), prelude_ident],
287                )?
288            }
289        }
290
291        // Import contract id. CONTRACT_ID is declared in the root module, so only import it into
292        // non-root modules
293        if self.current_package.is_contract_package() && self.current_mod_path.len() > 1 {
294            // import ::CONTRACT_ID
295            self.item_import_to_current_module(
296                handler,
297                engines,
298                &[Ident::new_no_span(package_name)],
299                &Ident::new_no_span(CONTRACT_ID.to_string()),
300                None,
301                Visibility::Private,
302            )?
303        }
304
305        Ok(())
306    }
307
308    pub(crate) fn enter_submodule(
309        &mut self,
310        handler: &Handler,
311        engines: &Engines,
312        mod_name: Ident,
313        visibility: Visibility,
314        module_span: Span,
315        check_implicits: bool,
316    ) -> Result<(), ErrorEmitted> {
317        let mut import_implicits = false;
318
319        // Ensure the new module exists and is initialized properly
320        if !self
321            .current_module()
322            .submodules()
323            .contains_key(&mod_name.to_string())
324            && check_implicits
325        {
326            // Entering a new module. Add a new one.
327            self.current_module_mut()
328                .add_new_submodule(&mod_name, visibility, Some(module_span));
329            import_implicits = true;
330        }
331
332        // Update self to point to the new module
333        self.current_mod_path.push(mod_name.clone());
334
335        // Import implicits into the newly created module.
336        if import_implicits {
337            self.import_implicits(handler, engines)?;
338        }
339
340        Ok(())
341    }
342
343    /// Pushes a new submodule to the namespace's module hierarchy.
344    pub fn push_submodule(
345        &mut self,
346        handler: &Handler,
347        engines: &Engines,
348        mod_name: Ident,
349        visibility: Visibility,
350        module_span: Span,
351        check_implicits: bool,
352    ) -> Result<(), ErrorEmitted> {
353        match self.enter_submodule(
354            handler,
355            engines,
356            mod_name,
357            visibility,
358            module_span,
359            check_implicits,
360        ) {
361            Ok(_) => Ok(()),
362            Err(e) => Err(e),
363        }
364    }
365
366    /// Pops the current submodule from the namespace's module hierarchy.
367    pub fn pop_submodule(&mut self) {
368        self.current_mod_path.pop();
369    }
370
371    ////// IMPORT //////
372
373    /// Given a path to a prelude in the standard library, create synonyms to every symbol in that
374    /// prelude to the current module.
375    ///
376    /// This is used when a new module is created in order to pupulate the module with implicit
377    /// imports from the standard library preludes.
378    ///
379    /// `src` is assumed to be absolute.
380    fn prelude_import(
381        &mut self,
382        handler: &Handler,
383        engines: &Engines,
384        src: &ModulePath,
385    ) -> Result<(), ErrorEmitted> {
386        let src_mod = self.require_module_from_absolute_path(handler, src)?;
387
388        let mut imports = vec![];
389
390        // A prelude should not declare its own items
391        assert!(src_mod.root_items().symbols.is_empty());
392
393        // Collect those item-imported items that the source module reexports
394        let mut symbols = src_mod
395            .root_items()
396            .use_item_synonyms
397            .keys()
398            .clone()
399            .collect::<Vec<_>>();
400        symbols.sort();
401        for symbol in symbols {
402            let (_, path, decl, src_visibility) = &src_mod.root_items().use_item_synonyms[symbol];
403            // Preludes reexport all their imports
404            assert!(matches!(src_visibility, Visibility::Public));
405            imports.push((symbol.clone(), decl.clone(), path.clone()))
406        }
407
408        // Collect those glob-imported items that the source module reexports.  There should be no
409        // name clashes in a prelude, so item reexports and glob reexports can be treated the same
410        // way.
411        let mut symbols = src_mod
412            .root_items()
413            .use_glob_synonyms
414            .keys()
415            .clone()
416            .collect::<Vec<_>>();
417        symbols.sort();
418        for symbol in symbols {
419            let bindings = &src_mod.root_items().use_glob_synonyms[symbol];
420            for (path, decl, src_visibility) in bindings.iter() {
421                // Preludes reexport all their imports.
422                assert!(matches!(src_visibility, Visibility::Public));
423                imports.push((symbol.clone(), decl.clone(), path.clone()))
424            }
425        }
426
427        let implemented_traits = src_mod.root_items().implemented_traits.clone();
428        let dst_mod = self.current_module_mut();
429
430        dst_mod
431            .current_items_mut()
432            .implemented_traits
433            .extend(implemented_traits, engines);
434
435        let dst_prelude_synonyms = &mut dst_mod.current_items_mut().prelude_synonyms;
436        imports.iter().for_each(|(symbol, decl, path)| {
437            // Preludes should not contain name clashes
438            assert!(!dst_prelude_synonyms.contains_key(symbol));
439            dst_prelude_synonyms.insert(symbol.clone(), (path.clone(), decl.clone()));
440        });
441
442        Ok(())
443    }
444
445    /// Given a path to a `src` module, create synonyms to every symbol in that module to the
446    /// current module.
447    ///
448    /// This is used when an import path contains an asterisk.
449    ///
450    /// `src` is assumed to be absolute.
451    pub(crate) fn star_import_to_current_module(
452        &mut self,
453        handler: &Handler,
454        engines: &Engines,
455        src: &ModulePath,
456        visibility: Visibility,
457    ) -> Result<(), ErrorEmitted> {
458        self.check_module_visibility(handler, src)?;
459
460        let src_mod = self.require_module_from_absolute_path(handler, src)?;
461
462        let mut decls_and_item_imports = vec![];
463
464        // Collect all items declared in the source module
465        let mut symbols = src_mod
466            .root_items()
467            .symbols
468            .keys()
469            .clone()
470            .collect::<Vec<_>>();
471        symbols.sort();
472        for symbol in symbols {
473            let decl = &src_mod.root_items().symbols[symbol];
474            if self.is_ancestor_of_current_module(src) || decl.visibility(engines).is_public() {
475                decls_and_item_imports.push((symbol.clone(), decl.clone(), src.to_vec()));
476            }
477        }
478        // Collect those item-imported items that the source module reexports
479        // These live in the same namespace as local declarations, so no shadowing is possible
480        let mut symbols = src_mod
481            .root_items()
482            .use_item_synonyms
483            .keys()
484            .clone()
485            .collect::<Vec<_>>();
486        symbols.sort();
487        for symbol in symbols {
488            let (_, path, decl, src_visibility) = &src_mod.root_items().use_item_synonyms[symbol];
489            if src_visibility.is_public() {
490                decls_and_item_imports.push((symbol.clone(), decl.clone(), path.clone()))
491            }
492        }
493
494        // Collect those glob-imported items that the source module reexports. These may be shadowed
495        // by local declarations and item imports in the source module, so they are treated
496        // separately.
497        let mut glob_imports = vec![];
498        let mut symbols = src_mod
499            .root_items()
500            .use_glob_synonyms
501            .keys()
502            .clone()
503            .collect::<Vec<_>>();
504        symbols.sort();
505        for symbol in symbols {
506            let bindings = &src_mod.root_items().use_glob_synonyms[symbol];
507            // Ignore if the symbol is shadowed by a local declaration or an item import in the source module
508            if !decls_and_item_imports
509                .iter()
510                .any(|(other_symbol, _, _)| symbol == other_symbol)
511            {
512                for (path, decl, src_visibility) in bindings.iter() {
513                    if src_visibility.is_public() {
514                        glob_imports.push((symbol.clone(), decl.clone(), path.clone()))
515                    }
516                }
517            }
518        }
519
520        let implemented_traits = src_mod.root_items().implemented_traits.clone();
521        let dst_mod = self.current_module_mut();
522
523        dst_mod
524            .current_items_mut()
525            .implemented_traits
526            .extend(implemented_traits, engines);
527
528        decls_and_item_imports
529            .iter()
530            .chain(glob_imports.iter())
531            .for_each(|(symbol, decl, path)| {
532                dst_mod.current_items_mut().insert_glob_use_symbol(
533                    engines,
534                    symbol.clone(),
535                    path.clone(),
536                    decl,
537                    visibility,
538                )
539            });
540
541        Ok(())
542    }
543
544    /// Pull all variants from the enum `enum_name` from the given `src` module and import them all into the `dst` module.
545    ///
546    /// Paths are assumed to be absolute.
547    pub(crate) fn variant_star_import_to_current_module(
548        &mut self,
549        handler: &Handler,
550        engines: &Engines,
551        src: &ModulePath,
552        enum_name: &Ident,
553        visibility: Visibility,
554    ) -> Result<(), ErrorEmitted> {
555        self.check_module_visibility(handler, src)?;
556
557        let parsed_decl_engine = engines.pe();
558        let decl_engine = engines.de();
559
560        let (decl, path) = self.item_lookup(handler, engines, enum_name, src, false)?;
561
562        match decl {
563            ResolvedDeclaration::Parsed(Declaration::EnumDeclaration(decl_id)) => {
564                let enum_decl = parsed_decl_engine.get_enum(&decl_id);
565
566                for variant in enum_decl.variants.iter() {
567                    let variant_name = &variant.name;
568                    let variant_decl =
569                        Declaration::EnumVariantDeclaration(EnumVariantDeclaration {
570                            enum_ref: decl_id,
571                            variant_name: variant_name.clone(),
572                            variant_decl_span: variant.span.clone(),
573                        });
574
575                    // import it this way.
576                    self.current_module_mut()
577                        .current_items_mut()
578                        .insert_glob_use_symbol(
579                            engines,
580                            variant_name.clone(),
581                            path.clone(),
582                            &ResolvedDeclaration::Parsed(variant_decl),
583                            visibility,
584                        );
585                }
586            }
587            ResolvedDeclaration::Typed(TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. })) => {
588                let enum_decl = decl_engine.get_enum(&decl_id);
589                let enum_ref = DeclRef::new(
590                    enum_decl.call_path.suffix.clone(),
591                    decl_id,
592                    enum_decl.span(),
593                );
594
595                for variant_decl in enum_decl.variants.iter() {
596                    let variant_name = &variant_decl.name;
597                    let decl =
598                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
599                            enum_ref: enum_ref.clone(),
600                            variant_name: variant_name.clone(),
601                            variant_decl_span: variant_decl.span.clone(),
602                        }));
603
604                    // import it this way.
605                    self.current_module_mut()
606                        .current_items_mut()
607                        .insert_glob_use_symbol(
608                            engines,
609                            variant_name.clone(),
610                            path.clone(),
611                            &decl,
612                            visibility,
613                        );
614                }
615            }
616            _ => {
617                return Err(handler.emit_err(CompileError::Internal(
618                    "Attempting to import variants of something that isn't an enum",
619                    enum_name.span(),
620                )));
621            }
622        };
623
624        Ok(())
625    }
626
627    /// Pull a single item from a `src` module and import it into the current module.
628    ///
629    /// The item we want to import is the last item in path because this is a `self` import.
630    pub(crate) fn self_import_to_current_module(
631        &mut self,
632        handler: &Handler,
633        engines: &Engines,
634        src: &ModulePath,
635        alias: Option<Ident>,
636        visibility: Visibility,
637    ) -> Result<(), ErrorEmitted> {
638        let (last_item, src) = src.split_last().expect("guaranteed by grammar");
639        self.item_import_to_current_module(handler, engines, src, last_item, alias, visibility)
640    }
641
642    /// Pull a single `item` from the given `src` module and import it into the current module.
643    ///
644    /// `src` is assumed to be absolute.
645    pub(crate) fn item_import_to_current_module(
646        &mut self,
647        handler: &Handler,
648        engines: &Engines,
649        src: &ModulePath,
650        item: &Ident,
651        alias: Option<Ident>,
652        visibility: Visibility,
653    ) -> Result<(), ErrorEmitted> {
654        self.check_module_visibility(handler, src)?;
655
656        let src_mod = self.require_module_from_absolute_path(handler, src)?;
657
658        let (decl, path) = self.item_lookup(handler, engines, item, src, false)?;
659
660        let mut impls_to_insert = TraitMap::default();
661        if decl.is_typed() {
662            // We only handle trait imports when handling typed declarations,
663            // that is, when performing type-checking, and not when collecting.
664            // Update this once the type system is updated to refer to parsed
665            // declarations.
666            //  if this is an enum or struct or function, import its implementations
667            if let Ok(type_id) = decl.return_type(&Handler::default(), engines) {
668                impls_to_insert.extend(
669                    src_mod
670                        .root_items()
671                        .implemented_traits
672                        .filter_by_type_item_import(type_id, engines),
673                    engines,
674                );
675            }
676            // if this is a trait, import its implementations
677            let decl_span = decl.span(engines);
678            if decl.is_trait() {
679                // TODO: we only import local impls from the source namespace
680                // this is okay for now but we'll need to device some mechanism to collect all available trait impls
681                impls_to_insert.extend(
682                    src_mod
683                        .root_items()
684                        .implemented_traits
685                        .filter_by_trait_decl_span(decl_span),
686                    engines,
687                );
688            }
689        }
690
691        // no matter what, import it this way though.
692        let dst_mod = self.current_module_mut();
693        let check_name_clash = |name| {
694            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
695                handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() });
696            }
697        };
698        match alias {
699            Some(alias) => {
700                check_name_clash(&alias);
701                dst_mod
702                    .current_items_mut()
703                    .use_item_synonyms
704                    .insert(alias.clone(), (Some(item.clone()), path, decl, visibility))
705            }
706            None => {
707                check_name_clash(item);
708                dst_mod
709                    .current_items_mut()
710                    .use_item_synonyms
711                    .insert(item.clone(), (None, path, decl, visibility))
712            }
713        };
714
715        dst_mod
716            .current_items_mut()
717            .implemented_traits
718            .extend(impls_to_insert, engines);
719
720        Ok(())
721    }
722
723    /// Pull a single variant `variant` from the enum `enum_name` from the given `src` module and
724    /// import it into the current module.
725    ///
726    /// `src` is assumed to be absolute.
727    #[allow(clippy::too_many_arguments)]
728    pub(crate) fn variant_import_to_current_module(
729        &mut self,
730        handler: &Handler,
731        engines: &Engines,
732        src: &ModulePath,
733        enum_name: &Ident,
734        variant_name: &Ident,
735        alias: Option<Ident>,
736        visibility: Visibility,
737    ) -> Result<(), ErrorEmitted> {
738        self.check_module_visibility(handler, src)?;
739
740        let decl_engine = engines.de();
741        let parsed_decl_engine = engines.pe();
742
743        let (decl, path) = self.item_lookup(handler, engines, enum_name, src, false)?;
744
745        match decl {
746            ResolvedDeclaration::Parsed(decl) => {
747                if let Declaration::EnumDeclaration(decl_id) = decl {
748                    let enum_decl = parsed_decl_engine.get_enum(&decl_id);
749
750                    if let Some(variant_decl) =
751                        enum_decl.variants.iter().find(|v| v.name == *variant_name)
752                    {
753                        // import it this way.
754                        let dst_mod = self.current_module_mut();
755                        let check_name_clash = |name| {
756                            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
757                                handler.emit_err(CompileError::ShadowsOtherSymbol {
758                                    name: name.into(),
759                                });
760                            }
761                        };
762
763                        match alias {
764                            Some(alias) => {
765                                check_name_clash(&alias);
766                                dst_mod.current_items_mut().use_item_synonyms.insert(
767                                    alias.clone(),
768                                    (
769                                        Some(variant_name.clone()),
770                                        path,
771                                        ResolvedDeclaration::Parsed(
772                                            Declaration::EnumVariantDeclaration(
773                                                EnumVariantDeclaration {
774                                                    enum_ref: decl_id,
775                                                    variant_name: variant_name.clone(),
776                                                    variant_decl_span: variant_decl.span.clone(),
777                                                },
778                                            ),
779                                        ),
780                                        visibility,
781                                    ),
782                                );
783                            }
784                            None => {
785                                check_name_clash(variant_name);
786                                dst_mod.current_items_mut().use_item_synonyms.insert(
787                                    variant_name.clone(),
788                                    (
789                                        None,
790                                        path,
791                                        ResolvedDeclaration::Parsed(
792                                            Declaration::EnumVariantDeclaration(
793                                                EnumVariantDeclaration {
794                                                    enum_ref: decl_id,
795                                                    variant_name: variant_name.clone(),
796                                                    variant_decl_span: variant_decl.span.clone(),
797                                                },
798                                            ),
799                                        ),
800                                        visibility,
801                                    ),
802                                );
803                            }
804                        };
805                    } else {
806                        return Err(handler.emit_err(CompileError::SymbolNotFound {
807                            name: variant_name.clone(),
808                            span: variant_name.span(),
809                        }));
810                    }
811                }
812            }
813            ResolvedDeclaration::Typed(decl) => {
814                if let TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. }) = decl {
815                    let enum_decl = decl_engine.get_enum(&decl_id);
816                    let enum_ref = DeclRef::new(
817                        enum_decl.call_path.suffix.clone(),
818                        decl_id,
819                        enum_decl.span(),
820                    );
821
822                    if let Some(variant_decl) =
823                        enum_decl.variants.iter().find(|v| v.name == *variant_name)
824                    {
825                        // import it this way.
826                        let dst_mod = self.current_module_mut();
827                        let check_name_clash = |name| {
828                            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
829                                handler.emit_err(CompileError::ShadowsOtherSymbol {
830                                    name: name.into(),
831                                });
832                            }
833                        };
834
835                        match alias {
836                            Some(alias) => {
837                                check_name_clash(&alias);
838                                dst_mod.current_items_mut().use_item_synonyms.insert(
839                                    alias.clone(),
840                                    (
841                                        Some(variant_name.clone()),
842                                        path,
843                                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(
844                                            ty::EnumVariantDecl {
845                                                enum_ref: enum_ref.clone(),
846                                                variant_name: variant_name.clone(),
847                                                variant_decl_span: variant_decl.span.clone(),
848                                            },
849                                        )),
850                                        visibility,
851                                    ),
852                                );
853                            }
854                            None => {
855                                check_name_clash(variant_name);
856                                dst_mod.current_items_mut().use_item_synonyms.insert(
857                                    variant_name.clone(),
858                                    (
859                                        None,
860                                        path,
861                                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(
862                                            ty::EnumVariantDecl {
863                                                enum_ref: enum_ref.clone(),
864                                                variant_name: variant_name.clone(),
865                                                variant_decl_span: variant_decl.span.clone(),
866                                            },
867                                        )),
868                                        visibility,
869                                    ),
870                                );
871                            }
872                        };
873                    } else {
874                        return Err(handler.emit_err(CompileError::SymbolNotFound {
875                            name: variant_name.clone(),
876                            span: variant_name.span(),
877                        }));
878                    }
879                } else {
880                    return Err(handler.emit_err(CompileError::Internal(
881                        "Attempting to import variants of something that isn't an enum",
882                        enum_name.span(),
883                    )));
884                }
885            }
886        };
887
888        Ok(())
889    }
890
891    /// Look up an item in the `src` module. Visibility is checked (if not ignored) from the current
892    /// module.
893    fn item_lookup(
894        &self,
895        handler: &Handler,
896        engines: &Engines,
897        item: &Ident,
898        src: &ModulePath,
899        ignore_visibility: bool,
900    ) -> Result<(ResolvedDeclaration, ModulePathBuf), ErrorEmitted> {
901        let src_mod = self.require_module_from_absolute_path(handler, src)?;
902        let src_items = src_mod.root_items();
903
904        let (decl, path, src_visibility) = if let Some(decl) = src_items.symbols.get(item) {
905            let visibility = if self.is_ancestor_of_current_module(src) {
906                Visibility::Public
907            } else {
908                decl.visibility(engines)
909            };
910            (decl.clone(), src.to_vec(), visibility)
911        } else if let Some((_, path, decl, reexport)) = src_items.use_item_synonyms.get(item) {
912            (decl.clone(), path.clone(), *reexport)
913        } else if let Some(decls) = src_items.use_glob_synonyms.get(item) {
914            if decls.len() == 1 {
915                let (path, decl, reexport) = &decls[0];
916                (decl.clone(), path.clone(), *reexport)
917            } else if decls.is_empty() {
918                return Err(handler.emit_err(CompileError::Internal(
919            "The name {symbol} was bound in a star import, but no corresponding module paths were found",
920            item.span(),
921                    )));
922            } else {
923                return Err(handler.emit_err(CompileError::SymbolWithMultipleBindings {
924                    name: item.clone(),
925                    paths: decls
926                        .iter()
927                        .map(|(path, decl, _)| {
928                            let mut path_strs = super::lexical_scope::get_path_for_decl(
929                                path,
930                                decl,
931                                engines,
932                                self.current_package_name(),
933                            );
934                            // Add the enum name to the path if the decl is an enum variant.
935                            if let TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
936                                enum_ref, ..
937                            }) = decl.expect_typed_ref()
938                            {
939                                path_strs.push(enum_ref.name().to_string())
940                            };
941                            path_strs.join("::")
942                        })
943                        .collect(),
944                    span: item.span(),
945                }));
946            }
947        } else {
948            // Symbol not found
949            return Err(handler.emit_err(CompileError::SymbolNotFound {
950                name: item.clone(),
951                span: item.span(),
952            }));
953        };
954
955        if !ignore_visibility && !src_visibility.is_public() {
956            handler.emit_err(CompileError::ImportPrivateSymbol {
957                name: item.clone(),
958                span: item.span(),
959            });
960        }
961
962        Ok((decl, path))
963    }
964
965    /// Check that all accessed modules in the src path are visible from the current module.
966    ///
967    /// Only the module part of the src path will be checked. If the src path contains identifiers
968    /// that refer to non-modules, e.g., enum names or associated types, then the visibility of
969    /// those items will not be checked.
970    ///
971    /// If src and the current module have a common ancestor module that is private, this privacy
972    /// modifier is ignored for visibility purposes, since src and the current module are both
973    /// behind that private visibility modifier.  Additionally, items in a private module are
974    /// visible to its immediate parent.
975    pub(crate) fn check_module_visibility(
976        &self,
977        handler: &Handler,
978        src: &ModulePath,
979    ) -> Result<(), ErrorEmitted> {
980        let dst = &self.current_mod_path;
981
982        // Calculate the number of src prefixes whose visibility is ignored.
983        let mut ignored_prefixes = 0;
984
985        // Ignore visibility of common ancestors
986        ignored_prefixes += src
987            .iter()
988            .zip(dst)
989            .position(|(src_id, dst_id)| src_id != dst_id)
990            .unwrap_or(dst.len());
991
992        // Ignore visibility of direct submodules of the destination module
993        if dst.len() == ignored_prefixes {
994            ignored_prefixes += 1;
995        }
996
997        // Check visibility of remaining submodules in the source path
998        for prefix in iter_prefixes(src).skip(ignored_prefixes) {
999            if let Some(module) = self.module_from_absolute_path(prefix) {
1000                if module.visibility().is_private() {
1001                    let prefix_last = prefix[prefix.len() - 1].clone();
1002                    handler.emit_err(CompileError::ImportPrivateModule {
1003                        span: prefix_last.span(),
1004                        name: prefix_last,
1005                    });
1006                }
1007            } else {
1008                return Ok(());
1009            }
1010        }
1011
1012        Ok(())
1013    }
1014
1015    fn is_ancestor_of_current_module(&self, src: &ModulePath) -> bool {
1016        let dst = &self.current_mod_path;
1017        dst.len() >= src.len() && src.iter().zip(dst).all(|(src, dst)| src == dst)
1018    }
1019}