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