use crate::{
ids::{ItemDefinitionId, ModuleId},
item_scope::BUILTIN_SCOPE,
module_tree::LocalModuleId,
package_defs::PackageDefs,
DefDatabase, Name, PackageId, Path, PathKind, PerNs, Visibility,
};
use std::iter::successors;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReachedFixedPoint {
Yes,
No,
}
#[derive(Debug, Clone)]
pub(crate) struct ResolvePathResult {
pub(crate) resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
pub(crate) segment_index: Option<usize>,
pub(crate) reached_fixedpoint: ReachedFixedPoint,
pub(crate) package: Option<PackageId>,
}
impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
}
fn with(
resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>,
package: Option<PackageId>,
) -> ResolvePathResult {
ResolvePathResult {
resolved_def,
segment_index,
reached_fixedpoint,
package,
}
}
}
impl PackageDefs {
pub(crate) fn resolve_path_in_module(
&self,
db: &dyn DefDatabase,
module: LocalModuleId,
path: &Path,
) -> (PerNs<(ItemDefinitionId, Visibility)>, Option<usize>) {
let res = self.resolve_path_with_fixedpoint(db, module, path);
(res.resolved_def, res.segment_index)
}
fn resolve_name_in_module(
&self,
_db: &dyn DefDatabase,
module: LocalModuleId,
name: &Name,
) -> PerNs<(ItemDefinitionId, Visibility)> {
self[module]
.get(name)
.or(BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none))
}
pub(crate) fn resolve_path_with_fixedpoint(
&self,
db: &dyn DefDatabase,
original_module: LocalModuleId,
path: &Path,
) -> ResolvePathResult {
let mut segments = path.segments.iter().enumerate();
let mut curr_per_ns: PerNs<(ItemDefinitionId, Visibility)> = match path.kind {
PathKind::Plain => {
let (_, segment) = match segments.next() {
Some((idx, segment)) => (idx, segment),
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
self.resolve_name_in_module(db, original_module, segment)
}
PathKind::Super(lvl) => {
let m = successors(Some(original_module), |m| self.module_tree[*m].parent)
.nth(lvl as usize);
if let Some(local_id) = m {
PerNs::types((
ModuleId {
package: self.module_tree.package,
local_id,
}
.into(),
Visibility::Public,
))
} else {
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
}
PathKind::Package => PerNs::types((
ModuleId {
package: self.module_tree.package,
local_id: self.module_tree.root,
}
.into(),
Visibility::Public,
)),
};
for (i, segment) in segments {
let (curr, vis) = match curr_per_ns.take_types() {
Some(r) => r,
None => {
return ResolvePathResult::empty(ReachedFixedPoint::No);
}
};
curr_per_ns = match curr {
ItemDefinitionId::ModuleId(module) => self[module.local_id].get(segment),
s => {
return ResolvePathResult::with(
PerNs::types((s, vis)),
ReachedFixedPoint::Yes,
Some(i),
Some(self.module_tree.package),
);
}
};
}
ResolvePathResult::with(
curr_per_ns,
ReachedFixedPoint::Yes,
None,
Some(self.module_tree.package),
)
}
}