use std::collections::HashMap;
use crate::graph::unified::build::phase4e_binding::BindingEdgeIndex;
use crate::graph::unified::mutation_target::GraphMutationTarget;
use crate::graph::unified::node::id::NodeId;
use crate::graph::unified::node::kind::NodeKind;
use super::arena::{Scope, ScopeArena, ScopeId, ScopeKind};
pub(crate) fn derive_scopes<G: GraphMutationTarget>(
graph: &G,
edge_index: &BindingEdgeIndex,
) -> ScopeArena {
let mut arena = ScopeArena::new();
let mut node_to_scope: HashMap<NodeId, ScopeId> = HashMap::default();
for (file_id, _path) in graph.files().iter() {
for &node_id in graph.indices().by_file(file_id) {
let Some(entry) = graph.nodes().get(node_id) else {
continue;
};
let Some(scope_kind) = node_kind_to_scope_kind(entry.kind) else {
continue;
};
let scope = Scope {
kind: scope_kind,
parent: ScopeId::INVALID, node: node_id,
byte_span: (entry.start_byte, entry.end_byte),
file: entry.file,
};
let scope_id = arena.allocate(scope);
node_to_scope.insert(node_id, scope_id);
}
}
let scope_ids: Vec<ScopeId> = node_to_scope.values().copied().collect();
for scope_id in scope_ids {
let scope_node = arena
.get(scope_id)
.expect("freshly allocated scope must exist")
.node;
let parent_scope_id =
enclosing_scope_id_via_index(scope_node, &node_to_scope, &edge_index.contains_parents);
arena
.set_parent(scope_id, parent_scope_id)
.expect("freshly allocated scope must accept set_parent");
}
arena
}
fn node_kind_to_scope_kind(kind: NodeKind) -> Option<ScopeKind> {
match kind {
NodeKind::Module => Some(ScopeKind::Module),
NodeKind::Function | NodeKind::Method => Some(ScopeKind::Function),
NodeKind::Class | NodeKind::Struct | NodeKind::Enum => Some(ScopeKind::Class),
NodeKind::Interface | NodeKind::Trait => Some(ScopeKind::Trait),
_ => None,
}
}
fn enclosing_scope_id_via_index(
start: NodeId,
map: &HashMap<NodeId, ScopeId>,
contains_parents: &HashMap<NodeId, NodeId>,
) -> ScopeId {
let mut current = start;
loop {
let Some(&parent) = contains_parents.get(¤t) else {
return ScopeId::INVALID;
};
if let Some(&scope_id) = map.get(&parent) {
return scope_id;
}
current = parent;
}
}