refactor(xatu): move ordering logic into rule

This commit is contained in:
2026-09-26 19:47:58 -04:00
parent c7456d837c
commit b43f6f294d
2 changed files with 71 additions and 67 deletions

View File

@@ -1,5 +1,63 @@
use crate::{ast, scope_graph::ScopeGraph};
use crate::{
ast,
scope_graph::{LetScope, NodeId, ScopeGraph},
};
pub fn check(graph: &ScopeGraph) -> Vec<ast::SourceRange> {
graph.out_of_order_bindings()
graph
.let_scopes()
.iter()
.flat_map(|let_scope| {
let expected = expected_order(graph, let_scope);
let positions = expected
.iter()
.enumerate()
.map(|(position, declaration)| (*declaration, position))
.collect::<Vec<_>>();
let_scope
.bindings
.iter()
.enumerate()
.filter_map(move |(position, binding)| {
positions
.iter()
.find(|(declaration, _)| *declaration == binding.declaration)
.filter(|(_, expected_position)| position != *expected_position)
.map(|_| binding.range)
})
})
.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);
}
}
}

View File

@@ -19,30 +19,8 @@ impl ScopeGraph {
graph
}
pub(crate) fn out_of_order_bindings(&self) -> Vec<ast::SourceRange> {
self.lets
.iter()
.flat_map(|let_scope| {
let expected = self.expected_order(let_scope);
let positions = expected
.iter()
.enumerate()
.map(|(position, declaration)| (*declaration, position))
.collect::<Vec<_>>();
let_scope
.bindings
.iter()
.enumerate()
.filter_map(move |(position, binding)| {
positions
.iter()
.find(|(declaration, _)| *declaration == binding.declaration)
.filter(|(_, expected_position)| position != *expected_position)
.map(|_| binding.range)
})
})
.collect()
pub(crate) fn let_scopes(&self) -> &[LetScope] {
&self.lets
}
fn build_expr(&mut self, expr: &ast::Expr, scope: NodeId) -> Vec<NodeId> {
@@ -102,39 +80,7 @@ impl ScopeGraph {
references
}
fn expected_order(&self, let_scope: &LetScope) -> Vec<NodeId> {
let mut order = Vec::new();
self.visit_references(&let_scope.entry_references, let_scope, &mut order);
order
}
fn visit_references(
&self,
references: &[NodeId],
let_scope: &LetScope,
order: &mut Vec<NodeId>,
) {
for reference in references {
for declaration in self.resolve(*reference) {
let Some(binding) = let_scope
.bindings
.iter()
.find(|binding| binding.declaration == declaration)
else {
continue;
};
if order.contains(&declaration) {
continue;
}
order.push(declaration);
self.visit_references(&binding.references, let_scope, order);
}
}
}
fn resolve(&self, reference: NodeId) -> Vec<NodeId> {
pub(crate) fn resolve(&self, reference: NodeId) -> Vec<NodeId> {
let Node::Reference(name) = &self.nodes[reference.0] else {
return Vec::new();
};
@@ -224,7 +170,7 @@ struct Declaration {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct NodeId(usize);
pub(crate) struct NodeId(usize);
#[derive(Debug)]
struct Edge {
@@ -233,14 +179,14 @@ struct Edge {
}
#[derive(Debug)]
struct LetScope {
bindings: Vec<Binding>,
entry_references: Vec<NodeId>,
pub(crate) struct LetScope {
pub(crate) bindings: Vec<Binding>,
pub(crate) entry_references: Vec<NodeId>,
}
#[derive(Debug)]
struct Binding {
declaration: NodeId,
references: Vec<NodeId>,
range: ast::SourceRange,
pub(crate) struct Binding {
pub(crate) declaration: NodeId,
pub(crate) references: Vec<NodeId>,
pub(crate) range: ast::SourceRange,
}