use crate::{
ids::ItemDefinitionId, module_tree::LocalModuleId, primitive_type::PrimitiveType,
visibility::Visibility, Name, PerNs,
};
use once_cell::sync::Lazy;
use rustc_hash::{FxHashMap, FxHashSet};
use std::collections::hash_map::Entry;
#[derive(Copy, Clone)]
pub(crate) enum ImportType {
Glob,
Named,
}
#[derive(Debug, Default)]
pub struct PerNsGlobImports {
types: FxHashSet<(LocalModuleId, Name)>,
values: FxHashSet<(LocalModuleId, Name)>,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct ItemScope {
types: FxHashMap<Name, (ItemDefinitionId, Visibility)>,
values: FxHashMap<Name, (ItemDefinitionId, Visibility)>,
defs: Vec<ItemDefinitionId>,
}
#[derive(Debug)]
pub(crate) struct AddResolutionFromImportResult {
pub changed: bool,
pub duplicate: bool,
}
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs<(ItemDefinitionId, Visibility)>>> =
Lazy::new(|| {
PrimitiveType::ALL
.iter()
.map(|(name, ty)| {
(
name.clone(),
PerNs::types(((*ty).into(), Visibility::Public)),
)
})
.collect()
});
impl ItemScope {
pub fn entries(
&self,
) -> impl Iterator<Item = (&'_ Name, PerNs<(ItemDefinitionId, Visibility)>)> + '_ {
let keys: FxHashSet<_> = self.types.keys().chain(self.values.keys()).collect();
keys.into_iter().map(move |name| (name, self.get(name)))
}
pub fn declarations(&self) -> impl Iterator<Item = ItemDefinitionId> + '_ {
self.defs.iter().copied()
}
pub(crate) fn add_definition(&mut self, def: ItemDefinitionId) {
self.defs.push(def)
}
pub(crate) fn add_resolution(
&mut self,
name: Name,
def: PerNs<(ItemDefinitionId, Visibility)>,
) -> bool {
let mut changed = false;
if let Some((types, visibility)) = def.types {
self.types.entry(name.clone()).or_insert_with(|| {
changed = true;
(types, visibility)
});
}
if let Some((values, visibility)) = def.values {
self.values.entry(name).or_insert_with(|| {
changed = true;
(values, visibility)
});
}
changed
}
pub(crate) fn add_resolution_from_import(
&mut self,
glob_imports: &mut PerNsGlobImports,
lookup: (LocalModuleId, Name),
def: PerNs<(ItemDefinitionId, Visibility)>,
def_import_type: ImportType,
) -> AddResolutionFromImportResult {
let mut changed = false;
let mut duplicate = false;
macro_rules! check_changed {
(
$changed:ident,
( $this:ident / $def:ident ) . $field:ident,
$glob_imports:ident [ $lookup:ident ],
$def_import_type:ident
) => {{
let existing = $this.$field.entry($lookup.1.clone());
match (existing, $def.$field) {
(Entry::Vacant(entry), Some(_)) => {
match $def_import_type {
ImportType::Glob => {
$glob_imports.$field.insert($lookup.clone());
}
ImportType::Named => {
$glob_imports.$field.remove(&$lookup);
}
}
if let Some(fld) = $def.$field {
entry.insert(fld);
}
$changed = true;
}
(Entry::Occupied(mut entry), Some(_))
if $glob_imports.$field.contains(&$lookup)
&& matches!($def_import_type, ImportType::Named) =>
{
$glob_imports.$field.remove(&$lookup);
if let Some(fld) = $def.$field {
entry.insert(fld);
}
$changed = true;
}
(Entry::Occupied(_), Some(_)) => {
let is_previous_from_glob = $glob_imports.$field.contains(&$lookup);
let is_explicit_import = matches!($def_import_type, ImportType::Named);
if is_explicit_import && !is_previous_from_glob {
duplicate = true;
}
}
_ => {}
}
}};
}
check_changed!(
changed,
(self / def).types,
glob_imports[lookup],
def_import_type
);
check_changed!(
changed,
(self / def).values,
glob_imports[lookup],
def_import_type
);
AddResolutionFromImportResult { changed, duplicate }
}
pub(crate) fn get(&self, name: &Name) -> PerNs<(ItemDefinitionId, Visibility)> {
PerNs {
types: self.types.get(name).copied(),
values: self.values.get(name).copied(),
}
}
}
impl PerNs<(ItemDefinitionId, Visibility)> {
pub(crate) fn from_definition(
def: ItemDefinitionId,
vis: Visibility,
has_constructor: bool,
) -> PerNs<(ItemDefinitionId, Visibility)> {
match def {
ItemDefinitionId::FunctionId(_) => PerNs::values((def, vis)),
ItemDefinitionId::StructId(_) => {
if has_constructor {
PerNs::both((def, vis), (def, vis))
} else {
PerNs::types((def, vis))
}
}
ItemDefinitionId::TypeAliasId(_) => PerNs::types((def, vis)),
ItemDefinitionId::PrimitiveType(_) => PerNs::types((def, vis)),
ItemDefinitionId::ModuleId(_) => PerNs::types((def, vis)),
}
}
}