Files
dev/src/natu/rules/let_order.rs

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);
}
}
}