88 lines
2.5 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|