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
}