use crate::ir::FileIr;
use crate::ir::cfg::RustCfgCondition;
use crate::resolution::rust::identity::index_of;
use crate::resolution::rust::snapshot::{RustResolutionSnapshot, RustSource};
use super::error::RustResolutionError;
#[derive(Clone, Copy)]
pub(super) struct NodeDeclaration {
pub(super) node: usize,
pub(super) definition: usize,
}
pub(super) struct Node {
pub(super) unit: usize,
pub(super) file: usize,
pub(super) scope: usize,
pub(super) parent: Option<usize>,
pub(super) declaration: Option<NodeDeclaration>,
pub(super) condition: RustCfgCondition,
}
pub(super) struct Graph {
pub(super) nodes: Box<[Node]>,
pub(super) roots: Box<[usize]>,
}
impl Graph {
pub(super) fn source<'a>(
&self,
snapshot: &'a RustResolutionSnapshot,
node: usize,
) -> Option<&'a RustSource> {
let file = self.nodes.get(node)?.file;
snapshot.sources().get(file)
}
pub(super) fn file_ir<'a>(
&self,
snapshot: &'a RustResolutionSnapshot,
node: usize,
) -> Option<&'a FileIr> {
self.source(snapshot, node).map(RustSource::ir)
}
pub(super) fn node_for_scope(&self, node: usize, file: usize, scope: usize) -> Option<usize> {
let mut current = Some(node);
while let Some(index) = current {
let found = self.nodes.get(index)?;
if found.file == file && found.scope == scope {
return Some(index);
}
current = found.parent;
}
None
}
}
pub(super) fn build(snapshot: &RustResolutionSnapshot) -> Result<Graph, RustResolutionError> {
let mut nodes: Vec<Node> = Vec::new();
let mut roots: Vec<usize> = Vec::new();
for (unit, instances) in snapshot.units().iter().enumerate() {
let offset = nodes.len();
roots.push(offset);
for instance in instances.modules() {
let parent = instance
.parent()
.map(|id| offset.saturating_add(index_of(id.index())));
let declaration = declaration_of(snapshot, &nodes, parent, instance.declaration);
nodes.push(Node {
unit,
file: file_index(snapshot, instance.path())?,
scope: index_of(instance.scope),
parent,
declaration,
condition: inherited(snapshot, &nodes, parent, declaration),
});
}
}
Ok(Graph {
nodes: nodes.into_boxed_slice(),
roots: roots.into_boxed_slice(),
})
}
fn declaration_of(
snapshot: &RustResolutionSnapshot,
nodes: &[Node],
parent: Option<usize>,
declaration: Option<u32>,
) -> Option<NodeDeclaration> {
let node = parent?;
let site = declaration_site(snapshot, nodes, node, declaration?)?;
Some(NodeDeclaration {
node,
definition: site.definition,
})
}
fn inherited(
snapshot: &RustResolutionSnapshot,
nodes: &[Node],
parent: Option<usize>,
declaration: Option<NodeDeclaration>,
) -> RustCfgCondition {
let above = parent
.and_then(|node| nodes.get(node))
.map(|node| node.condition.clone())
.unwrap_or_default();
let stated = declaration
.and_then(|found| declared_condition(snapshot, nodes, found))
.unwrap_or_default();
above.and(&stated)
}
fn declared_condition(
snapshot: &RustResolutionSnapshot,
nodes: &[Node],
declaration: NodeDeclaration,
) -> Option<RustCfgCondition> {
let ir = file_ir_of(snapshot, nodes, declaration.node)?;
ir.module_declarations
.iter()
.find(|site| site.definition == declaration.definition)
.map(|site| site.condition().clone())
}
fn declaration_site<'a>(
snapshot: &'a RustResolutionSnapshot,
nodes: &[Node],
node: usize,
declaration: u32,
) -> Option<&'a crate::ir::sites::ModuleDeclarationSite> {
file_ir_of(snapshot, nodes, node)?
.module_declarations
.get(index_of(declaration))
}
fn file_ir_of<'a>(
snapshot: &'a RustResolutionSnapshot,
nodes: &[Node],
node: usize,
) -> Option<&'a FileIr> {
let file = nodes.get(node)?.file;
snapshot.sources().get(file).map(RustSource::ir)
}
fn file_index(snapshot: &RustResolutionSnapshot, path: &str) -> Result<usize, RustResolutionError> {
snapshot
.sources()
.binary_search_by(|source| source.path().cmp(path))
.map_err(|_| RustResolutionError::UnknownFile {
file: Box::from(path),
})
}