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

88 lines
2.5 KiB
Rust

use crate::{
ast,
rules::is_suppressed,
scope_graph::{LetScope, NodeId, ScopeGraph},
};
pub struct Finding {
pub range: ast::SourceRange,
pub expected_order: Vec<String>,
}
pub fn check(graph: &ScopeGraph, source: &str) -> Vec<Finding> {
graph
.let_scopes()
.iter()
.flat_map(|let_scope| {
let expected = expected_order(graph, let_scope)
.into_iter()
.filter(|declaration| !binding_suppressed(let_scope, *declaration, source))
.collect::<Vec<_>>();
let actual = let_scope
.bindings
.iter()
.filter(|binding| !is_suppressed(source, binding.range, "let_order"))
.map(|binding| binding.declaration)
.collect::<Vec<_>>();
(!actual.starts_with(&expected)).then(|| Finding {
range: let_scope.range,
expected_order: expected
.iter()
.map(|declaration| binding_name(let_scope, *declaration).to_owned())
.collect(),
})
})
.collect()
}
fn binding_suppressed(let_scope: &LetScope, declaration: NodeId, source: &str) -> bool {
let binding = let_scope
.bindings
.iter()
.find(|binding| binding.declaration == declaration)
.expect("let declaration");
is_suppressed(source, binding.range, "let_order")
}
fn binding_name(let_scope: &LetScope, declaration: NodeId) -> &str {
&let_scope
.bindings
.iter()
.find(|binding| binding.declaration == declaration)
.expect("let declaration")
.name
}
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);
}
}
}