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