Skip to main content

texform_transform/rewrite/
contract.rs

1//! Eliminated-form contract checked after the full transform pipeline.
2
3use crate::ast::Ast;
4use crate::parse::ParseContext;
5use crate::rewrite::rule::RuleTargetKey;
6use crate::rewrite::scheduler::{node_name_for_target, target_present};
7
8/// A single eliminated-form contract violation found in an AST.
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct ContractViolation {
11    /// The eliminated form that is still present.
12    pub target: RuleTargetKey,
13    /// Best-effort node name for human-facing diagnostics.
14    pub node_name: Option<String>,
15}
16
17impl std::fmt::Display for ContractViolation {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        write!(
20            f,
21            "contract violation for {} `{}` (node {:?})",
22            self.target.kind_label(),
23            self.target.name,
24            self.node_name
25        )
26    }
27}
28
29/// Collects all eliminated-form contract violations currently present in `ast`.
30pub fn collect_eliminated_violations(
31    ast: &Ast,
32    parse_ctx: &ParseContext,
33    eliminated_forms: &[RuleTargetKey],
34) -> Vec<ContractViolation> {
35    let mut violations = Vec::new();
36
37    for node_id in ast.find_all(ast.root(), |_| true) {
38        for target in eliminated_forms {
39            if target_present(ast, node_id, *target, parse_ctx) {
40                let node_name = node_name_for_target(ast, node_id);
41                violations.push(ContractViolation {
42                    target: *target,
43                    node_name,
44                });
45            }
46        }
47    }
48
49    violations
50}