use crate::{ ast, rules::is_suppressed, scope_graph::{LetScope, NodeId, ScopeGraph}, }; pub struct Finding { pub range: ast::SourceRange, pub expected_order: Vec, } pub fn check(graph: &ScopeGraph, source: &str) -> Vec { 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::>(); let actual = let_scope .bindings .iter() .filter(|binding| !is_suppressed(source, binding.range, "let_order")) .map(|binding| binding.declaration) .collect::>(); (!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 { 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, ) { 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); } } }