sway_core/semantic_analysis/namespace/
root.rs

1use std::fmt;
2
3use super::{module::Module, trait_map::TraitMap, Ident, ModuleName};
4use crate::{
5    decl_engine::{DeclEngine, DeclRef},
6    engine_threading::*,
7    language::{
8        parsed::*,
9        ty::{self, StructDecl, TyDecl},
10        Visibility,
11    },
12    namespace::{ModulePath, ModulePathBuf},
13    TypeId,
14};
15use rustc_hash::FxHasher;
16use std::hash::BuildHasherDefault;
17use sway_error::{
18    error::CompileError,
19    handler::{ErrorEmitted, Handler},
20};
21use sway_types::{span::Span, ProgramId, Spanned};
22use sway_utils::iter_prefixes;
23
24#[derive(Clone, Debug)]
25pub enum ResolvedDeclaration {
26    Parsed(Declaration),
27    Typed(ty::TyDecl),
28}
29
30impl DisplayWithEngines for ResolvedDeclaration {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
32        match self {
33            ResolvedDeclaration::Parsed(decl) => DisplayWithEngines::fmt(decl, f, engines),
34            ResolvedDeclaration::Typed(decl) => DisplayWithEngines::fmt(decl, f, engines),
35        }
36    }
37}
38
39impl DebugWithEngines for ResolvedDeclaration {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
41        match self {
42            ResolvedDeclaration::Parsed(decl) => DebugWithEngines::fmt(decl, f, engines),
43            ResolvedDeclaration::Typed(decl) => DebugWithEngines::fmt(decl, f, engines),
44        }
45    }
46}
47
48impl PartialEqWithEngines for ResolvedDeclaration {
49    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
50        match (self, other) {
51            (ResolvedDeclaration::Parsed(lhs), ResolvedDeclaration::Parsed(rhs)) => {
52                lhs.eq(rhs, ctx)
53            }
54            (ResolvedDeclaration::Typed(lhs), ResolvedDeclaration::Typed(rhs)) => lhs.eq(rhs, ctx),
55            // TODO: Right now we consider differently represented resolved declarations to not be
56            // equal. This is only used for comparing paths when doing imports, and we will be able
57            // to safely remove it once we introduce normalized paths.
58            (ResolvedDeclaration::Parsed(_lhs), ResolvedDeclaration::Typed(_rhs)) => false,
59            (ResolvedDeclaration::Typed(_lhs), ResolvedDeclaration::Parsed(_rhs)) => false,
60        }
61    }
62}
63
64impl ResolvedDeclaration {
65    pub fn is_typed(&self) -> bool {
66        match self {
67            ResolvedDeclaration::Parsed(_) => false,
68            ResolvedDeclaration::Typed(_) => true,
69        }
70    }
71
72    pub fn resolve_parsed(self, decl_engine: &DeclEngine) -> Declaration {
73        match self {
74            ResolvedDeclaration::Parsed(decl) => decl,
75            ResolvedDeclaration::Typed(ty_decl) => ty_decl
76                .get_parsed_decl(decl_engine)
77                .expect("expecting valid parsed declaration"),
78        }
79    }
80
81    pub fn expect_parsed(self) -> Declaration {
82        match self {
83            ResolvedDeclaration::Parsed(decl) => decl,
84            ResolvedDeclaration::Typed(_ty_decl) => panic!(),
85        }
86    }
87
88    pub fn expect_typed(self) -> ty::TyDecl {
89        match self {
90            ResolvedDeclaration::Parsed(_) => panic!(),
91            ResolvedDeclaration::Typed(ty_decl) => ty_decl,
92        }
93    }
94
95    pub fn expect_typed_ref(&self) -> &ty::TyDecl {
96        match self {
97            ResolvedDeclaration::Parsed(_) => panic!(),
98            ResolvedDeclaration::Typed(ty_decl) => ty_decl,
99        }
100    }
101
102    pub(crate) fn to_struct_decl(
103        &self,
104        handler: &Handler,
105        engines: &Engines,
106    ) -> Result<ResolvedDeclaration, ErrorEmitted> {
107        match self {
108            ResolvedDeclaration::Parsed(decl) => decl
109                .to_struct_decl(handler, engines)
110                .map(|id| ResolvedDeclaration::Parsed(Declaration::StructDeclaration(id))),
111            ResolvedDeclaration::Typed(decl) => decl.to_struct_decl(handler, engines).map(|id| {
112                ResolvedDeclaration::Typed(TyDecl::StructDecl(StructDecl { decl_id: id }))
113            }),
114        }
115    }
116
117    pub(crate) fn visibility(&self, engines: &Engines) -> Visibility {
118        match self {
119            ResolvedDeclaration::Parsed(decl) => decl.visibility(engines.pe()),
120            ResolvedDeclaration::Typed(decl) => decl.visibility(engines.de()),
121        }
122    }
123
124    fn span(&self, engines: &Engines) -> sway_types::Span {
125        match self {
126            ResolvedDeclaration::Parsed(decl) => decl.span(engines),
127            ResolvedDeclaration::Typed(decl) => decl.span(engines),
128        }
129    }
130
131    pub(crate) fn return_type(
132        &self,
133        handler: &Handler,
134        engines: &Engines,
135    ) -> Result<TypeId, ErrorEmitted> {
136        match self {
137            ResolvedDeclaration::Parsed(_decl) => unreachable!(),
138            ResolvedDeclaration::Typed(decl) => decl.return_type(handler, engines),
139        }
140    }
141
142    fn is_trait(&self) -> bool {
143        match self {
144            ResolvedDeclaration::Parsed(decl) => {
145                matches!(decl, Declaration::TraitDeclaration(_))
146            }
147            ResolvedDeclaration::Typed(decl) => {
148                matches!(decl, TyDecl::TraitDecl(_))
149            }
150        }
151    }
152}
153
154/// The root module, from which all other module dependencies can be accessed.
155///
156/// This is equivalent to the "crate root" of a Rust crate.
157///
158/// We use a custom type for the `Root` in order to ensure that methods that only work with
159/// canonical paths, or that use canonical paths internally, are *only* called from the root. This
160/// normally includes methods that first lookup some canonical path via `use_synonyms` before using
161/// that canonical path to look up the symbol declaration.
162#[derive(Clone, Debug)]
163pub struct Root {
164    // The contents of the package being compiled.
165    current_package: Module,
166    // Program id for the package.
167    program_id: ProgramId,
168    // True if the current package is a contract, false otherwise.
169    is_contract_package: bool,
170    // The external dependencies of the current package. Note that an external package is
171    // represented as a `Root` object. This is because external packages may have their own external
172    // dependencies which are needed for lookups, but which are not directly accessible to the
173    // current package.
174    external_packages: im::HashMap<ModuleName, Root, BuildHasherDefault<FxHasher>>,
175}
176
177impl Root {
178    // Create a new root object with a root module in the current package.
179    //
180    // To ensure the correct initialization the factory functions `package_root_without_contract_id`
181    // and `package_root_with_contract_id` are supplied in `contract_helpers`.
182    //
183    // External packages must be added afterwards by calling `add_external`
184    pub fn new(
185        package_name: Ident,
186        span: Option<Span>,
187        program_id: ProgramId,
188        is_contract_package: bool,
189    ) -> Self {
190        // The root module must be public
191        let module = Module::new(package_name, Visibility::Public, span, &vec![]);
192        Self {
193            current_package: module,
194            program_id,
195            is_contract_package,
196            external_packages: Default::default(),
197        }
198    }
199
200    // Add an external package to this package. The package name must be supplied, since the package
201    // may be referred to by a different name in the forc.toml file than the actual name of the
202    // package.
203    pub fn add_external(&mut self, package_name: String, external_package: Root) {
204        // This should be ensured by the package manager
205        assert!(!self.external_packages.contains_key(&package_name));
206        self.external_packages
207            .insert(package_name, external_package);
208    }
209
210    pub(crate) fn get_external_package(&self, package_name: &String) -> Option<&Root> {
211        self.external_packages.get(package_name)
212    }
213
214    pub(super) fn exists_as_external(&self, package_name: &String) -> bool {
215        self.get_external_package(package_name).is_some()
216    }
217
218    pub fn external_packages(
219        &self,
220    ) -> &im::HashMap<ModuleName, Root, BuildHasherDefault<FxHasher>> {
221        &self.external_packages
222    }
223
224    pub fn current_package_root_module(&self) -> &Module {
225        &self.current_package
226    }
227
228    pub fn current_package_root_module_mut(&mut self) -> &mut Module {
229        &mut self.current_package
230    }
231
232    pub fn current_package_name(&self) -> &Ident {
233        self.current_package.name()
234    }
235
236    pub fn program_id(&self) -> ProgramId {
237        self.program_id
238    }
239
240    fn check_path_is_in_current_package(&self, mod_path: &ModulePathBuf) -> bool {
241        !mod_path.is_empty() && mod_path[0] == *self.current_package.name()
242    }
243
244    fn package_relative_path(mod_path: &ModulePathBuf) -> ModulePathBuf {
245        mod_path[1..].to_vec()
246    }
247
248    pub(super) fn is_contract_package(&self) -> bool {
249        self.is_contract_package
250    }
251
252    // Find module in the current environment. `mod_path` must be a fully qualified path
253    pub fn module_from_absolute_path(&self, mod_path: &ModulePathBuf) -> Option<&Module> {
254        assert!(!mod_path.is_empty());
255        let package_relative_path = Self::package_relative_path(mod_path);
256        if mod_path[0] == *self.current_package.name() {
257            self.current_package.submodule(&package_relative_path)
258        } else if let Some(external_package) = self.external_packages.get(&mod_path[0].to_string())
259        {
260            external_package
261                .current_package_root_module()
262                .submodule(&package_relative_path)
263        } else {
264            None
265        }
266    }
267
268    // Find module in the current environment. `mod_path` must be a fully qualified path.
269    // Throw an error if the module doesn't exist
270    pub(crate) fn require_module(
271        &self,
272        handler: &Handler,
273        mod_path: &ModulePathBuf,
274    ) -> Result<&Module, ErrorEmitted> {
275        if mod_path.is_empty() {
276            return Err(handler.emit_err(CompileError::Internal(
277                "Found empty absolute mod path",
278                Span::dummy(),
279            )));
280        }
281        let is_in_current_package = self.check_path_is_in_current_package(mod_path);
282        match self.module_from_absolute_path(mod_path) {
283            Some(module) => Ok(module),
284            None => Err(handler.emit_err(crate::namespace::module::module_not_found(
285                mod_path,
286                is_in_current_package,
287            ))),
288        }
289    }
290
291    // Find a module in the current package. `mod_path` must be a fully qualified path
292    pub(super) fn module_in_current_package(&self, mod_path: &ModulePathBuf) -> Option<&Module> {
293        assert!(self.check_path_is_in_current_package(mod_path));
294        self.module_from_absolute_path(mod_path)
295    }
296
297    // Find mutable module in the current environment. `mod_path` must be a fully qualified path
298    pub(super) fn module_mut_from_absolute_path(
299        &mut self,
300        mod_path: &ModulePathBuf,
301    ) -> Option<&mut Module> {
302        assert!(!mod_path.is_empty());
303        let package_relative_path = Self::package_relative_path(mod_path);
304        if *self.current_package.name() == mod_path[0] {
305            self.current_package.submodule_mut(&package_relative_path)
306        } else if let Some(external_package) =
307            self.external_packages.get_mut(&mod_path[0].to_string())
308        {
309            external_package.module_mut_in_current_package(&package_relative_path)
310        } else {
311            None
312        }
313    }
314
315    // Find mutable module in the current environment. `mod_path` must be a fully qualified path.
316    // Throw an error if the module doesn't exist
317    pub(super) fn require_module_mut(
318        &mut self,
319        handler: &Handler,
320        mod_path: &ModulePathBuf,
321    ) -> Result<&mut Module, ErrorEmitted> {
322        let is_in_current_package = self.check_path_is_in_current_package(mod_path);
323        match self.module_mut_from_absolute_path(mod_path) {
324            Some(module) => Ok(module),
325            None => Err(handler.emit_err(crate::namespace::module::module_not_found(
326                mod_path,
327                is_in_current_package,
328            ))),
329        }
330    }
331
332    // Find a mutable module in the current package. `mod_path` must be a fully qualified path
333    pub(super) fn module_mut_in_current_package(
334        &mut self,
335        mod_path: &ModulePathBuf,
336    ) -> Option<&mut Module> {
337        assert!(self.check_path_is_in_current_package(mod_path));
338        self.module_mut_from_absolute_path(mod_path)
339    }
340
341    // Find a mutable module in the current package. `mod_path` must be a fully qualified path
342    // Throw an error if the module doesn't exist
343    pub(super) fn require_module_mut_in_current_package(
344        &mut self,
345        handler: &Handler,
346        mod_path: &ModulePathBuf,
347    ) -> Result<&mut Module, ErrorEmitted> {
348        assert!(self.check_path_is_in_current_package(mod_path));
349        self.require_module_mut(handler, mod_path)
350    }
351
352    ////// IMPORT //////
353
354    /// Given a path to a `src` module, create synonyms to every symbol in that module to the given
355    /// `dst` module.
356    ///
357    /// This is used when an import path contains an asterisk.
358    ///
359    /// Paths are assumed to be absolute.
360    pub fn star_import(
361        &mut self,
362        handler: &Handler,
363        engines: &Engines,
364        src: &ModulePath,
365        dst: &ModulePath,
366        visibility: Visibility,
367    ) -> Result<(), ErrorEmitted> {
368        self.check_module_privacy(handler, src, dst)?;
369
370        let src_mod = self.require_module(handler, &src.to_vec())?;
371
372        let mut decls_and_item_imports = vec![];
373
374        // Collect all items declared in the source module
375        let mut symbols = src_mod
376            .root_items()
377            .symbols
378            .keys()
379            .clone()
380            .collect::<Vec<_>>();
381        symbols.sort();
382        for symbol in symbols {
383            let decl = &src_mod.root_items().symbols[symbol];
384            if is_ancestor(src, dst) || decl.visibility(engines).is_public() {
385                decls_and_item_imports.push((symbol.clone(), decl.clone(), src.to_vec()));
386            }
387        }
388        // Collect those item-imported items that the source module reexports
389        // These live in the same namespace as local declarations, so no shadowing is possible
390        let mut symbols = src_mod
391            .root_items()
392            .use_item_synonyms
393            .keys()
394            .clone()
395            .collect::<Vec<_>>();
396        symbols.sort();
397        for symbol in symbols {
398            let (_, path, decl, src_visibility) = &src_mod.root_items().use_item_synonyms[symbol];
399            if src_visibility.is_public() {
400                decls_and_item_imports.push((symbol.clone(), decl.clone(), path.clone()))
401            }
402        }
403
404        // Collect those glob-imported items that the source module reexports. These may be shadowed
405        // by local declarations and item imports in the source module, so they are treated
406        // separately.
407        let mut glob_imports = vec![];
408        let mut symbols = src_mod
409            .root_items()
410            .use_glob_synonyms
411            .keys()
412            .clone()
413            .collect::<Vec<_>>();
414        symbols.sort();
415        for symbol in symbols {
416            let bindings = &src_mod.root_items().use_glob_synonyms[symbol];
417            // Ignore if the symbol is shadowed by a local declaration or an item import in the source module
418            if !decls_and_item_imports
419                .iter()
420                .any(|(other_symbol, _, _)| symbol == other_symbol)
421            {
422                for (path, decl, src_visibility) in bindings.iter() {
423                    if src_visibility.is_public() {
424                        glob_imports.push((symbol.clone(), decl.clone(), path.clone()))
425                    }
426                }
427            }
428        }
429
430        let implemented_traits = src_mod.root_items().implemented_traits.clone();
431        let dst_mod = self.require_module_mut_in_current_package(handler, &dst.to_vec())?;
432
433        dst_mod
434            .current_items_mut()
435            .implemented_traits
436            .extend(implemented_traits, engines);
437
438        decls_and_item_imports
439            .iter()
440            .chain(glob_imports.iter())
441            .for_each(|(symbol, decl, path)| {
442                dst_mod.current_items_mut().insert_glob_use_symbol(
443                    engines,
444                    symbol.clone(),
445                    path.clone(),
446                    decl,
447                    visibility,
448                )
449            });
450
451        Ok(())
452    }
453
454    /// Pull a single item from a `src` module and import it into the `dst` module.
455    ///
456    /// The item we want to import is basically the last item in path because this is a `self`
457    /// import.
458    pub(crate) fn self_import(
459        &mut self,
460        handler: &Handler,
461        engines: &Engines,
462        src: &ModulePath,
463        dst: &ModulePath,
464        alias: Option<Ident>,
465        visibility: Visibility,
466    ) -> Result<(), ErrorEmitted> {
467        let (last_item, src) = src.split_last().expect("guaranteed by grammar");
468        self.item_import(handler, engines, src, last_item, dst, alias, visibility)
469    }
470
471    pub(super) fn item_lookup(
472        &self,
473        handler: &Handler,
474        engines: &Engines,
475        item: &Ident,
476        src: &ModulePath,
477        dst: &ModulePath,
478        ignore_visibility: bool,
479    ) -> Result<(ResolvedDeclaration, ModulePathBuf), ErrorEmitted> {
480        let src_mod = self.require_module(handler, &src.to_vec())?;
481        let src_items = src_mod.root_items();
482
483        let (decl, path, src_visibility) = if let Some(decl) = src_items.symbols.get(item) {
484            let visibility = if is_ancestor(src, dst) {
485                Visibility::Public
486            } else {
487                decl.visibility(engines)
488            };
489            (decl.clone(), src.to_vec(), visibility)
490        } else if let Some((_, path, decl, reexport)) = src_items.use_item_synonyms.get(item) {
491            (decl.clone(), path.clone(), *reexport)
492        } else if let Some(decls) = src_items.use_glob_synonyms.get(item) {
493            if decls.len() == 1 {
494                let (path, decl, reexport) = &decls[0];
495                (decl.clone(), path.clone(), *reexport)
496            } else if decls.is_empty() {
497                return Err(handler.emit_err(CompileError::Internal(
498            "The name {symbol} was bound in a star import, but no corresponding module paths were found",
499            item.span(),
500                    )));
501            } else {
502                return Err(handler.emit_err(CompileError::SymbolWithMultipleBindings {
503                    name: item.clone(),
504                    paths: decls
505                        .iter()
506                        .map(|(path, decl, _)| {
507                            let mut path_strs = super::lexical_scope::get_path_for_decl(
508                                path,
509                                decl,
510                                engines,
511                                self.current_package_name(),
512                            );
513                            // Add the enum name to the path if the decl is an enum variant.
514                            if let TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
515                                enum_ref, ..
516                            }) = decl.expect_typed_ref()
517                            {
518                                path_strs.push(enum_ref.name().to_string())
519                            };
520                            path_strs.join("::")
521                        })
522                        .collect(),
523                    span: item.span(),
524                }));
525            }
526        } else {
527            // Symbol not found
528            return Err(handler.emit_err(CompileError::SymbolNotFound {
529                name: item.clone(),
530                span: item.span(),
531            }));
532        };
533
534        if !ignore_visibility && !src_visibility.is_public() {
535            handler.emit_err(CompileError::ImportPrivateSymbol {
536                name: item.clone(),
537                span: item.span(),
538            });
539        }
540
541        Ok((decl, path))
542    }
543
544    /// Pull a single `item` from the given `src` module and import it into the `dst` module.
545    ///
546    /// Paths are assumed to be absolute.
547    #[allow(clippy::too_many_arguments)]
548    pub(crate) fn item_import(
549        &mut self,
550        handler: &Handler,
551        engines: &Engines,
552        src: &ModulePath,
553        item: &Ident,
554        dst: &ModulePath,
555        alias: Option<Ident>,
556        visibility: Visibility,
557    ) -> Result<(), ErrorEmitted> {
558        self.check_module_privacy(handler, src, dst)?;
559        let src_mod = self.require_module(handler, &src.to_vec())?;
560
561        let (decl, path) = self.item_lookup(handler, engines, item, src, dst, false)?;
562
563        let mut impls_to_insert = TraitMap::default();
564        if decl.is_typed() {
565            // We only handle trait imports when handling typed declarations,
566            // that is, when performing type-checking, and not when collecting.
567            // Update this once the type system is updated to refer to parsed
568            // declarations.
569            //  if this is an enum or struct or function, import its implementations
570            if let Ok(type_id) = decl.return_type(&Handler::default(), engines) {
571                impls_to_insert.extend(
572                    src_mod
573                        .root_items()
574                        .implemented_traits
575                        .filter_by_type_item_import(type_id, engines),
576                    engines,
577                );
578            }
579            // if this is a trait, import its implementations
580            let decl_span = decl.span(engines);
581            if decl.is_trait() {
582                // TODO: we only import local impls from the source namespace
583                // this is okay for now but we'll need to device some mechanism to collect all available trait impls
584                impls_to_insert.extend(
585                    src_mod
586                        .root_items()
587                        .implemented_traits
588                        .filter_by_trait_decl_span(decl_span),
589                    engines,
590                );
591            }
592        }
593
594        // no matter what, import it this way though.
595        let dst_mod = self.require_module_mut_in_current_package(handler, &dst.to_vec())?;
596        let check_name_clash = |name| {
597            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
598                handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() });
599            }
600        };
601        match alias {
602            Some(alias) => {
603                check_name_clash(&alias);
604                dst_mod
605                    .current_items_mut()
606                    .use_item_synonyms
607                    .insert(alias.clone(), (Some(item.clone()), path, decl, visibility))
608            }
609            None => {
610                check_name_clash(item);
611                dst_mod
612                    .current_items_mut()
613                    .use_item_synonyms
614                    .insert(item.clone(), (None, path, decl, visibility))
615            }
616        };
617
618        dst_mod
619            .current_items_mut()
620            .implemented_traits
621            .extend(impls_to_insert, engines);
622
623        Ok(())
624    }
625
626    /// Pull a single variant `variant` from the enum `enum_name` from the given `src` module and import it into the `dst` module.
627    ///
628    /// Paths are assumed to be absolute.
629    #[allow(clippy::too_many_arguments)] // TODO: remove lint bypass once private modules are no longer experimental
630    pub(crate) fn variant_import(
631        &mut self,
632        handler: &Handler,
633        engines: &Engines,
634        src: &ModulePath,
635        enum_name: &Ident,
636        variant_name: &Ident,
637        dst: &ModulePath,
638        alias: Option<Ident>,
639        visibility: Visibility,
640    ) -> Result<(), ErrorEmitted> {
641        self.check_module_privacy(handler, src, dst)?;
642
643        let decl_engine = engines.de();
644        let parsed_decl_engine = engines.pe();
645
646        let (decl, path) = self.item_lookup(handler, engines, enum_name, src, dst, false)?;
647
648        match decl {
649            ResolvedDeclaration::Parsed(decl) => {
650                if let Declaration::EnumDeclaration(decl_id) = decl {
651                    let enum_decl = parsed_decl_engine.get_enum(&decl_id);
652
653                    if let Some(variant_decl) =
654                        enum_decl.variants.iter().find(|v| v.name == *variant_name)
655                    {
656                        // import it this way.
657                        let dst_mod =
658                            self.require_module_mut_in_current_package(handler, &dst.to_vec())?;
659                        let check_name_clash = |name| {
660                            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
661                                handler.emit_err(CompileError::ShadowsOtherSymbol {
662                                    name: name.into(),
663                                });
664                            }
665                        };
666
667                        match alias {
668                            Some(alias) => {
669                                check_name_clash(&alias);
670                                dst_mod.current_items_mut().use_item_synonyms.insert(
671                                    alias.clone(),
672                                    (
673                                        Some(variant_name.clone()),
674                                        path,
675                                        ResolvedDeclaration::Parsed(
676                                            Declaration::EnumVariantDeclaration(
677                                                EnumVariantDeclaration {
678                                                    enum_ref: decl_id,
679                                                    variant_name: variant_name.clone(),
680                                                    variant_decl_span: variant_decl.span.clone(),
681                                                },
682                                            ),
683                                        ),
684                                        visibility,
685                                    ),
686                                );
687                            }
688                            None => {
689                                check_name_clash(variant_name);
690                                dst_mod.current_items_mut().use_item_synonyms.insert(
691                                    variant_name.clone(),
692                                    (
693                                        None,
694                                        path,
695                                        ResolvedDeclaration::Parsed(
696                                            Declaration::EnumVariantDeclaration(
697                                                EnumVariantDeclaration {
698                                                    enum_ref: decl_id,
699                                                    variant_name: variant_name.clone(),
700                                                    variant_decl_span: variant_decl.span.clone(),
701                                                },
702                                            ),
703                                        ),
704                                        visibility,
705                                    ),
706                                );
707                            }
708                        };
709                    } else {
710                        return Err(handler.emit_err(CompileError::SymbolNotFound {
711                            name: variant_name.clone(),
712                            span: variant_name.span(),
713                        }));
714                    }
715                }
716            }
717            ResolvedDeclaration::Typed(decl) => {
718                if let TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. }) = decl {
719                    let enum_decl = decl_engine.get_enum(&decl_id);
720                    let enum_ref = DeclRef::new(
721                        enum_decl.call_path.suffix.clone(),
722                        decl_id,
723                        enum_decl.span(),
724                    );
725
726                    if let Some(variant_decl) =
727                        enum_decl.variants.iter().find(|v| v.name == *variant_name)
728                    {
729                        // import it this way.
730                        let dst_mod =
731                            self.require_module_mut_in_current_package(handler, &dst.to_vec())?;
732                        let check_name_clash = |name| {
733                            if dst_mod.current_items().use_item_synonyms.contains_key(name) {
734                                handler.emit_err(CompileError::ShadowsOtherSymbol {
735                                    name: name.into(),
736                                });
737                            }
738                        };
739
740                        match alias {
741                            Some(alias) => {
742                                check_name_clash(&alias);
743                                dst_mod.current_items_mut().use_item_synonyms.insert(
744                                    alias.clone(),
745                                    (
746                                        Some(variant_name.clone()),
747                                        path,
748                                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(
749                                            ty::EnumVariantDecl {
750                                                enum_ref: enum_ref.clone(),
751                                                variant_name: variant_name.clone(),
752                                                variant_decl_span: variant_decl.span.clone(),
753                                            },
754                                        )),
755                                        visibility,
756                                    ),
757                                );
758                            }
759                            None => {
760                                check_name_clash(variant_name);
761                                dst_mod.current_items_mut().use_item_synonyms.insert(
762                                    variant_name.clone(),
763                                    (
764                                        None,
765                                        path,
766                                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(
767                                            ty::EnumVariantDecl {
768                                                enum_ref: enum_ref.clone(),
769                                                variant_name: variant_name.clone(),
770                                                variant_decl_span: variant_decl.span.clone(),
771                                            },
772                                        )),
773                                        visibility,
774                                    ),
775                                );
776                            }
777                        };
778                    } else {
779                        return Err(handler.emit_err(CompileError::SymbolNotFound {
780                            name: variant_name.clone(),
781                            span: variant_name.span(),
782                        }));
783                    }
784                } else {
785                    return Err(handler.emit_err(CompileError::Internal(
786                        "Attempting to import variants of something that isn't an enum",
787                        enum_name.span(),
788                    )));
789                }
790            }
791        };
792
793        Ok(())
794    }
795
796    /// Pull all variants from the enum `enum_name` from the given `src` module and import them all into the `dst` module.
797    ///
798    /// Paths are assumed to be absolute.
799    pub(crate) fn variant_star_import(
800        &mut self,
801        handler: &Handler,
802        engines: &Engines,
803        src: &ModulePath,
804        dst: &ModulePath,
805        enum_name: &Ident,
806        visibility: Visibility,
807    ) -> Result<(), ErrorEmitted> {
808        self.check_module_privacy(handler, src, dst)?;
809
810        let parsed_decl_engine = engines.pe();
811        let decl_engine = engines.de();
812
813        let (decl, path) = self.item_lookup(handler, engines, enum_name, src, dst, false)?;
814
815        match decl {
816            ResolvedDeclaration::Parsed(Declaration::EnumDeclaration(decl_id)) => {
817                let enum_decl = parsed_decl_engine.get_enum(&decl_id);
818
819                for variant in enum_decl.variants.iter() {
820                    let variant_name = &variant.name;
821                    let variant_decl =
822                        Declaration::EnumVariantDeclaration(EnumVariantDeclaration {
823                            enum_ref: decl_id,
824                            variant_name: variant_name.clone(),
825                            variant_decl_span: variant.span.clone(),
826                        });
827
828                    // import it this way.
829                    self.require_module_mut_in_current_package(handler, &dst.to_vec())?
830                        .current_items_mut()
831                        .insert_glob_use_symbol(
832                            engines,
833                            variant_name.clone(),
834                            path.clone(),
835                            &ResolvedDeclaration::Parsed(variant_decl),
836                            visibility,
837                        );
838                }
839            }
840            ResolvedDeclaration::Typed(TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. })) => {
841                let enum_decl = decl_engine.get_enum(&decl_id);
842                let enum_ref = DeclRef::new(
843                    enum_decl.call_path.suffix.clone(),
844                    decl_id,
845                    enum_decl.span(),
846                );
847
848                for variant_decl in enum_decl.variants.iter() {
849                    let variant_name = &variant_decl.name;
850                    let decl =
851                        ResolvedDeclaration::Typed(TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
852                            enum_ref: enum_ref.clone(),
853                            variant_name: variant_name.clone(),
854                            variant_decl_span: variant_decl.span.clone(),
855                        }));
856
857                    // import it this way.
858                    self.require_module_mut_in_current_package(handler, &dst.to_vec())?
859                        .current_items_mut()
860                        .insert_glob_use_symbol(
861                            engines,
862                            variant_name.clone(),
863                            path.clone(),
864                            &decl,
865                            visibility,
866                        );
867                }
868            }
869            _ => {
870                return Err(handler.emit_err(CompileError::Internal(
871                    "Attempting to import variants of something that isn't an enum",
872                    enum_name.span(),
873                )));
874            }
875        };
876
877        Ok(())
878    }
879
880    /// Check that all accessed modules in the src path are visible from the dst path.
881    ///
882    /// Only the module part of the src path will be checked. If the src path contains identifiers
883    /// that refer to non-modules, e.g., enum names or associated types, then the visibility of
884    /// those items will not be checked.
885    ///
886    /// If src and dst have a common ancestor module that is private, this privacy modifier is
887    /// ignored for visibility purposes, since src and dst are both behind that private visibility
888    /// modifier.  Additionally, items in a private module are visible to its immediate parent.
889    ///
890    /// The returned path is the part of the src path that refers to modules.
891    pub(crate) fn check_module_privacy(
892        &self,
893        handler: &Handler,
894        src: &ModulePath,
895        dst: &ModulePath,
896    ) -> Result<(), ErrorEmitted> {
897        // Calculate the number of src prefixes whose visibility is ignored.
898        let mut ignored_prefixes = 0;
899
900        // Ignore visibility of common ancestors
901        ignored_prefixes += src
902            .iter()
903            .zip(dst)
904            .position(|(src_id, dst_id)| src_id != dst_id)
905            .unwrap_or(dst.len());
906
907        // Ignore visibility of direct submodules of the destination module
908        if dst.len() == ignored_prefixes {
909            ignored_prefixes += 1;
910        }
911
912        // Check visibility of remaining submodules in the source path
913        for prefix in iter_prefixes(src).skip(ignored_prefixes) {
914            if let Some(module) = self.module_from_absolute_path(&prefix.to_vec()) {
915                if module.visibility().is_private() {
916                    let prefix_last = prefix[prefix.len() - 1].clone();
917                    handler.emit_err(CompileError::ImportPrivateModule {
918                        span: prefix_last.span(),
919                        name: prefix_last,
920                    });
921                }
922            } else {
923                return Ok(());
924            }
925        }
926
927        Ok(())
928    }
929}
930
931fn is_ancestor(src: &ModulePath, dst: &ModulePath) -> bool {
932    dst.len() >= src.len() && src.iter().zip(dst).all(|(src, dst)| src == dst)
933}