use crate::{ ast, scope_graph::{LetScope, NodeId, ScopeGraph}, }; pub struct Finding { pub range: ast::SourceRange, pub expected_order: Vec, } pub fn check(graph: &ScopeGraph) -> Vec { 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 { 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); } } }