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