weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
//! Boundary tests that keep reference-only reference oracles out of Weir production paths.

use std::fs;
use std::path::{Path, PathBuf};

const CPU_ORACLE_CALLS: &[&str] = &[
    "build_cpu_reference(",
    "callgraph_build_cpu(",
    "compute_bitmap_bytes(",
    "compute_cpu(",
    "cpu_ref(",
    "cpu_ref_into(",
    "cpu_ref_sharded(",
    "cpu_subset_closure(",
    "ifds_reach_closure_cpu(",
    "live_closure_cpu(",
    "loop_summarize_cpu(",
    "range_propagate_cpu(",
    "reaching_closure_cpu(",
    "slice_closure_cpu(",
    "solve_cpu(",
    "summarize_function_cpu(",
];

const CPU_ORACLE_PATH_REFERENCES: &[&str] = &[
    "crate::bitset_closure_oracle::",
    "crate::ifds_cpu_oracle::",
    "crate::oracle::",
    "self::oracle::",
    "super::oracle::",
    "use crate::bitset_closure_oracle",
    "use crate::ifds_cpu_oracle",
    "use crate::oracle",
    "use self::oracle",
    "use super::oracle",
    "use weir::oracle",
    "weir::oracle::",
    "mod cpu_oracle;",
    "mod oracle;",
    "use cpu_oracle::",
];

#[path = "oracle_boundary/bitset_tail_contracts.rs"]
mod bitset_tail_contracts;
#[path = "oracle_boundary/cpu_oracle_boundary_contracts.rs"]
mod cpu_oracle_boundary_contracts;
#[path = "oracle_boundary/duplicate_boundary_contracts.rs"]
mod duplicate_boundary_contracts;
#[path = "oracle_boundary/raw_ir_containment_contracts.rs"]
mod raw_ir_containment_contracts;

fn collect_rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
    for entry in fs::read_dir(dir).expect("weir src directory must be readable") {
        let entry = entry.expect("weir src entry must be readable");
        let path = entry.path();
        if path.is_dir() {
            collect_rust_files(&path, out);
        } else if path.extension().and_then(|ext| ext.to_str()) == Some("rs") {
            out.push(path);
        }
    }
}

fn is_oracle_path(path: &Path) -> bool {
    path.components()
        .any(|component| component.as_os_str().to_str() == Some("oracle"))
        || path
            .file_name()
            .and_then(|name| name.to_str())
            .is_some_and(|name| {
                name == "oracle.rs" || name == "cpu_oracle.rs" || name.ends_with("_cpu_oracle.rs")
            })
}

fn is_test_source_path(path: &Path) -> bool {
    path.components()
        .filter_map(|component| component.as_os_str().to_str())
        .any(|name| name == "tests" || name.ends_with("_tests") || name == "test_harness")
        || path
            .file_name()
            .and_then(|name| name.to_str())
            .is_some_and(|name| name == "tests.rs" || name.ends_with("_tests.rs"))
}

fn function_body<'a>(text: &'a str, name: &str) -> &'a str {
    let start = find_function_start(text, name).unwrap_or_else(|| panic!("{name} must exist"));
    let body_open = text[start..]
        .find('{')
        .map(|offset| start + offset)
        .unwrap_or_else(|| panic!("{name} must have a function body"));
    let mut depth = 0i32;
    for (offset, ch) in text[body_open..].char_indices() {
        match ch {
            '{' => depth += 1,
            '}' => {
                depth -= 1;
                if depth == 0 {
                    return &text[body_open..body_open + offset + ch.len_utf8()];
                }
            }
            _ => {}
        }
    }
    panic!("{name} function body must have balanced braces");
}

fn find_function_start(text: &str, name: &str) -> Option<usize> {
    let needle = format!("fn {name}");
    let mut search_from = 0usize;
    while let Some(relative) = text[search_from..].find(&needle) {
        let start = search_from + relative;
        let after_name = start + needle.len();
        let exact = text[after_name..]
            .chars()
            .next()
            .is_none_or(|ch| !is_rust_ident_continue(ch));
        if exact {
            return Some(start);
        }
        search_from = after_name;
    }
    None
}

fn is_rust_ident_continue(ch: char) -> bool {
    ch == '_' || ch.is_ascii_alphanumeric()
}

fn scan_source_file(path: &Path, text: &str, violations: &mut Vec<String>) {
    let mut pending_reference_fn = false;
    let mut deprecated_attr_body = false;
    let mut reference_depth: Option<i32> = None;
    let mut cfg_test_depth: Option<i32> = None;

    for (idx, line) in text.lines().enumerate() {
        let trimmed = line.trim();
        if deprecated_attr_body {
            pending_reference_fn |= trimmed.contains("reference oracle only;");
            deprecated_attr_body = !trimmed.contains(")]");
            continue;
        }
        if trimmed.starts_with("#[deprecated") {
            pending_reference_fn = trimmed.contains("reference oracle only;");
            deprecated_attr_body = !trimmed.contains(")]");
            continue;
        }
        if trimmed.starts_with("#[cfg(test)]") {
            cfg_test_depth = Some(0);
        }
        if pending_reference_fn
            && (trimmed.starts_with("pub fn ") || trimmed.starts_with("pub(crate) fn "))
        {
            reference_depth = Some(0);
            pending_reference_fn = false;
        }

        let allowed = reference_depth.is_some() || cfg_test_depth.is_some();
        if !allowed && CPU_ORACLE_CALLS.iter().any(|needle| line.contains(needle)) {
            violations.push(format!("{}:{}: {}", path.display(), idx + 1, trimmed));
        }

        let delta = brace_delta(line);
        if let Some(depth) = reference_depth.as_mut() {
            *depth += delta;
            if *depth <= 0 && line.contains('}') {
                reference_depth = None;
            }
        }
        if let Some(depth) = cfg_test_depth.as_mut() {
            *depth += delta;
            if *depth <= 0 && line.contains('}') {
                cfg_test_depth = None;
            }
        }
    }
}

fn scan_oracle_definitions(path: &Path, text: &str, violations: &mut Vec<String>) {
    let lines = text.lines().collect::<Vec<_>>();
    for (idx, line) in lines.iter().enumerate() {
        let trimmed = line.trim();
        if !is_cpu_oracle_definition(trimmed) {
            continue;
        }
        if !trimmed.starts_with("pub(crate) fn ") {
            violations.push(format!(
                "{}:{}: CPU/reference helper must be pub(crate): {}",
                path.display(),
                idx + 1,
                trimmed
            ));
            continue;
        }
        let marker_start = idx.saturating_sub(3);
        let context = lines[marker_start..idx].join("\n");
        let has_reference_marker =
            context.contains("#[deprecated") && context.contains("reference oracle only;");
        if !has_reference_marker {
            violations.push(format!(
                "{}:{}: CPU/reference helper missing reference-only deprecation marker: {}",
                path.display(),
                idx + 1,
                trimmed
            ));
        }
    }
}

fn scan_oracle_path_references(path: &Path, text: &str, violations: &mut Vec<String>) {
    let lines = text.lines().collect::<Vec<_>>();
    for (idx, line) in lines.iter().enumerate() {
        let code = line.split_once("//").map_or(*line, |(code, _)| code);
        let trimmed = code.trim();
        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }
        if !CPU_ORACLE_PATH_REFERENCES
            .iter()
            .any(|needle| trimmed.contains(needle))
        {
            continue;
        }
        if has_cpu_parity_cfg_context(&lines, idx) {
            continue;
        }
        violations.push(format!("{}:{}: {}", path.display(), idx + 1, trimmed));
    }
}

fn has_cpu_parity_cfg_context(lines: &[&str], line_idx: usize) -> bool {
    let start = line_idx.saturating_sub(10);
    lines[start..line_idx].iter().any(|line| {
        let trimmed = line.trim();
        trimmed.starts_with("#[cfg")
            && (trimmed.contains("feature = \"cpu-parity\"") || trimmed.contains("test"))
    })
}

fn is_cpu_oracle_definition(line: &str) -> bool {
    (line.starts_with("pub fn ") || line.starts_with("pub(crate) fn "))
        && [
            "build_cpu_reference",
            "callgraph_build_cpu",
            "compute_cpu",
            "cpu_ref",
            "cpu_ref_sharded",
            "cpu_subset_closure",
            "ifds_reach_closure_cpu",
            "live_closure_cpu",
            "loop_summarize_cpu",
            "range_propagate_cpu",
            "reaching_closure_cpu",
            "reference_sharded",
            "slice_closure_cpu",
            "solve_cpu",
            "summarize_function_cpu",
        ]
        .iter()
        .any(|name| line.contains(name))
}

fn brace_delta(line: &str) -> i32 {
    let mut delta = 0i32;
    for ch in line.chars() {
        match ch {
            '{' => delta += 1,
            '}' => delta -= 1,
            _ => {}
        }
    }
    delta
}