72 lines
2.0 KiB
Rust
72 lines
2.0 KiB
Rust
use crate::{
|
|
ast,
|
|
scope_graph::{LetScope, NodeId, ScopeGraph},
|
|
};
|
|
|
|
pub struct Finding {
|
|
pub range: ast::SourceRange,
|
|
pub expected_order: Vec<String>,
|
|
}
|
|
|
|
pub fn check(graph: &ScopeGraph) -> Vec<Finding> {
|
|
graph
|
|
.let_scopes()
|
|
.iter()
|
|
.flat_map(|let_scope| {
|
|
let expected = expected_order(graph, let_scope);
|
|
let is_ordered = expected.iter().enumerate().all(|(position, declaration)| {
|
|
let_scope
|
|
.bindings
|
|
.get(position)
|
|
.is_some_and(|binding| binding.declaration == *declaration)
|
|
});
|
|
|
|
(!is_ordered).then(|| Finding {
|
|
range: let_scope.range,
|
|
expected_order: expected
|
|
.iter()
|
|
.filter_map(|declaration| {
|
|
let_scope
|
|
.bindings
|
|
.iter()
|
|
.find(|binding| binding.declaration == *declaration)
|
|
.map(|binding| binding.name.clone())
|
|
})
|
|
.collect(),
|
|
})
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn expected_order(graph: &ScopeGraph, let_scope: &LetScope) -> Vec<NodeId> {
|
|
let mut order = Vec::new();
|
|
visit_references(graph, &let_scope.entry_references, let_scope, &mut order);
|
|
order
|
|
}
|
|
|
|
fn visit_references(
|
|
graph: &ScopeGraph,
|
|
references: &[NodeId],
|
|
let_scope: &LetScope,
|
|
order: &mut Vec<NodeId>,
|
|
) {
|
|
for reference in references {
|
|
for declaration in graph.resolve(*reference) {
|
|
let Some(binding) = let_scope
|
|
.bindings
|
|
.iter()
|
|
.find(|binding| binding.declaration == declaration)
|
|
else {
|
|
continue;
|
|
};
|
|
|
|
if order.contains(&declaration) {
|
|
continue;
|
|
}
|
|
|
|
order.push(declaration);
|
|
visit_references(graph, &binding.references, let_scope, order);
|
|
}
|
|
}
|
|
}
|