use std::{ops::ControlFlow, slice};
use super::{
lookup::{NamespaceAncestors, NamespaceCandidates},
*,
};
pub(super) fn path(name: &TsEntityName) -> AliasPath {
if let TsEntityName::Ident(root) = name {
return AliasPath {
root: root.to_id(),
members: AliasMembers::Empty,
};
}
if let TsEntityName::TsQualifiedName(qualified) = name {
if let TsEntityName::Ident(root) = &qualified.left {
return AliasPath {
root: root.to_id(),
members: AliasMembers::Single(qualified.right.sym.clone().into()),
};
}
}
fn collect(name: &TsEntityName, members: &mut Vec<Wtf8Atom>) -> Id {
match name {
TsEntityName::Ident(ident) => ident.to_id(),
TsEntityName::TsQualifiedName(qualified) => {
let root = collect(&qualified.left, members);
members.push(qualified.right.sym.clone().into());
root
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}
let mut members = Vec::new();
let root = collect(name, &mut members);
AliasPath {
root,
members: AliasMembers::Qualified(members.into_boxed_slice()),
}
}
impl TsBindings {
pub(super) fn resolve_aliases(&mut self) {
if self.aliases.is_empty() {
return;
}
self.alias_states = AliasResolver::new(self, false).resolve_all().0;
}
#[doc(hidden)]
pub fn with_namespace_aliases(mut self) -> Self {
if self.aliases.is_empty() {
return self;
}
let (states, roots) = AliasResolver::new(&self, true).resolve_all();
for (alias, root) in self.aliases.iter_mut().zip(roots) {
if let (Some(path), Some(root)) = (&mut alias.path, root) {
let id = &self.declarations[root.index()].id;
if path.root != *id {
path.root.clone_from(id);
}
}
}
self.alias_states = states;
self
}
}
type AliasResolution = ControlFlow<TsAliasId, Option<TsValueTarget>>;
struct AliasFrame<'a> {
alias: TsAliasId,
members: slice::Iter<'a, Wtf8Atom>,
root: Option<Box<AliasRoot<'a>>>,
}
struct AliasRoot<'a> {
candidates: NamespaceCandidates<'a, NamespaceAncestors<'a>>,
current: Option<TsDeclarationId>,
original: Option<TsDeclarationId>,
space: TsReferenceSpace,
}
struct AliasResolver<'a> {
bindings: &'a TsBindings,
states: Vec<AliasState>,
pending: Vec<AliasFrame<'a>>,
roots: Vec<Option<TsDeclarationId>>,
}
impl<'a> AliasResolver<'a> {
fn new(bindings: &'a TsBindings, complete_roots: bool) -> Self {
Self {
bindings,
states: vec![AliasState::Pending; bindings.aliases.len()],
pending: Vec::new(),
roots: if complete_roots {
vec![None; bindings.aliases.len()]
} else {
Vec::new()
},
}
}
fn resolve_all(mut self) -> (Vec<AliasState>, Vec<Option<TsDeclarationId>>) {
let bindings = self.bindings;
for alias in bindings.aliases() {
self.resolve(alias);
}
(self.states, self.roots)
}
fn declaration(&self, declaration: TsDeclarationId) -> AliasResolution {
let declaration = self.bindings.canonical_value_declaration(declaration);
let Some(alias) = self.bindings.declaration_alias(declaration) else {
return ControlFlow::Continue(Some(TsValueTarget::Binding(declaration)));
};
match self.states[alias.index()] {
AliasState::Pending => ControlFlow::Break(alias),
AliasState::Resolved(target) => ControlFlow::Continue(Some(target)),
AliasState::Resolving | AliasState::Unknown => ControlFlow::Continue(None),
}
}
fn resolve(&mut self, alias: TsAliasId) {
if !matches!(self.states[alias.index()], AliasState::Pending) {
return;
}
let Some((mut frame, mut resolution)) = self.start(alias) else {
return;
};
loop {
match resolution {
ControlFlow::Break(dependency) => {
if let Some((next_frame, next_resolution)) = self.start(dependency) {
self.pending.push(frame);
frame = next_frame;
resolution = next_resolution;
} else {
resolution = ControlFlow::Continue(None);
}
}
ControlFlow::Continue(result) => {
if let Some(root) = &mut frame.root {
resolution = match self.namespace_root(root) {
ControlFlow::Break(dependency) => ControlFlow::Break(dependency),
ControlFlow::Continue(root) => {
self.roots[frame.alias.index()] = root;
frame.root = None;
root.map_or(ControlFlow::Continue(None), |root| {
self.declaration(root)
})
}
};
continue;
}
if let Some(target) = result {
if let Some(name) = frame.members.next() {
resolution = self.member(target, name);
continue;
}
}
self.states[frame.alias.index()] = match result {
Some(target) => AliasState::Resolved(target),
None => AliasState::Unknown,
};
let Some(parent) = self.pending.pop() else {
break;
};
frame = parent;
}
}
}
}
fn start(&mut self, alias: TsAliasId) -> Option<(AliasFrame<'a>, AliasResolution)> {
self.states[alias.index()] = AliasState::Unknown;
let declaration = self.bindings.alias(alias);
if declaration.is_type_only {
return None;
}
let path = declaration.path.as_ref()?;
let original = self.bindings.declaration_id(&path.root);
let mut frame = AliasFrame {
alias,
members: path.members.as_slice().iter(),
root: None,
};
let resolution = if self.roots.is_empty() {
let original = original?;
self.states[alias.index()] = AliasState::Resolving;
self.declaration(original)
} else {
let space = if path.members.as_slice().is_empty() {
TsReferenceSpace::Any
} else {
TsReferenceSpace::Namespace
};
let mut root = AliasRoot {
candidates: NamespaceCandidates::new(
self.bindings,
&path.root.0,
path.root.1,
NamespaceAncestors {
bindings: self.bindings,
next: self.bindings.declarations[declaration.declaration.index()].owner,
},
space,
),
current: None,
original,
space,
};
self.states[alias.index()] = AliasState::Resolving;
match self.namespace_root(&mut root) {
ControlFlow::Break(dependency) => {
frame.root = Some(Box::new(root));
ControlFlow::Break(dependency)
}
ControlFlow::Continue(root) => {
self.roots[alias.index()] = root;
root.map_or(ControlFlow::Continue(None), |root| self.declaration(root))
}
}
};
Some((frame, resolution))
}
fn namespace_root(
&self,
root: &mut AliasRoot<'a>,
) -> ControlFlow<TsAliasId, Option<TsDeclarationId>> {
loop {
let Some(declaration) = root.current.or_else(|| root.candidates.next()) else {
return ControlFlow::Continue(root.original);
};
root.current = Some(declaration);
let source = self.bindings.declarations[declaration.index()]
.export_target
.unwrap_or(declaration);
let eligible = if root.space == TsReferenceSpace::Any {
true
} else if let Some(alias) = self.bindings.declaration_alias(source) {
match self.states[alias.index()] {
AliasState::Pending => return ControlFlow::Break(alias),
AliasState::Resolved(target) => {
self.bindings.target_has_space(target, root.space)
}
AliasState::Resolving | AliasState::Unknown => true,
}
} else {
self.bindings
.target_has_space(TsValueTarget::Binding(source), root.space)
};
if eligible {
return ControlFlow::Continue(Some(declaration));
}
root.current = None;
}
}
fn member(&self, target: TsValueTarget, name: &Wtf8Atom) -> AliasResolution {
let TsValueTarget::Binding(declaration) = target else {
return ControlFlow::Continue(None);
};
let Some(container) = self.bindings.declaration_container(declaration) else {
return ControlFlow::Continue(None);
};
let Some(member_id) = self.bindings.named_member(container, name) else {
return ControlFlow::Continue(None);
};
let member = self.bindings.member(member_id);
if member.is_enum_member() {
return ControlFlow::Continue(Some(TsValueTarget::EnumMember(member_id)));
}
match member.value.or(member.erased) {
Some(declaration) => self.declaration(declaration),
None => ControlFlow::Continue(None),
}
}
}