use std::fs;
use std::path::{Path, PathBuf};
const BANNED: &[(&str, &str)] = &[
(
"abs_diff",
"raw abs_diff on a timestamp (use clock33::signed_distance)",
),
("1 << 33", "hand-rolled 2^33 (use clock33::WRAP_33BIT)"),
("1u64 << 33", "hand-rolled 2^33 (use clock33::WRAP_33BIT)"),
(
"0x2_0000_0000",
"hand-rolled 2^33 (use clock33::WRAP_33BIT)",
),
("0x200000000", "hand-rolled 2^33 (use clock33::WRAP_33BIT)"),
(
"8_589_934_592",
"hand-rolled 2^33 (use clock33::WRAP_33BIT)",
),
(
"27_000_000",
"hand-rolled 27 MHz clock rate (use clock33::PCR_HZ)",
),
(
"* 300",
"hand-rolled PCR base*300+ext arithmetic (use clock33::pcr_from_parts / pcr_split / PCR_WRAP_27MHZ)",
),
];
const ALLOW: &[(&str, usize, &str, &str)] = &[(
"src/splice.rs",
98,
"abs_diff",
"condition_splice_point is the documented LINEAR (non-wrapping) variant for a clock that does not wrap; \
the wrapping clock uses condition_splice_point_wrapping (circular distance, issue #1125)",
)];
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in fs::read_dir(dir).expect("read src dir") {
let path = entry.expect("dir entry").path();
if path.is_dir() {
rust_files(&path, out);
} else if path.extension().is_some_and(|e| e == "rs") {
out.push(path);
}
}
}
fn non_test_lines(src: &str) -> Vec<(usize, String)> {
let mut out = Vec::new();
let mut lines = src.lines().enumerate().peekable();
while let Some((no, line)) = lines.next() {
let t = line.trim_start();
if t.starts_with("#[cfg(test)]") || t.starts_with("#[cfg(all(test") {
let mut depth = 0i32;
let mut opened = false;
for (_, item) in lines.by_ref() {
for c in item.chars() {
match c {
'{' => {
depth += 1;
opened = true;
}
'}' => depth -= 1,
_ => {}
}
}
if (opened && depth <= 0) || (!opened && item.trim_end().ends_with(';')) {
break;
}
}
continue;
}
if t.starts_with("//") {
continue;
}
out.push((no + 1, line.to_string()));
}
out
}
fn banned_in_line(line: &str) -> Vec<(&'static str, &'static str)> {
BANNED
.iter()
.filter(|(needle, _)| line.contains(needle))
.copied()
.collect()
}
#[test]
fn no_hand_rolled_33bit_wrap_arithmetic_in_src() {
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut files = Vec::new();
rust_files(&src_dir, &mut files);
assert!(!files.is_empty());
let mut violations = Vec::new();
let mut used = vec![false; ALLOW.len()];
for file in files {
let text = fs::read_to_string(&file).expect("read source");
let rel = file
.strip_prefix(env!("CARGO_MANIFEST_DIR"))
.unwrap()
.to_string_lossy()
.replace('\\', "/");
for (no, line) in non_test_lines(&text) {
for (needle, why) in banned_in_line(&line) {
if let Some(i) = ALLOW
.iter()
.position(|(s, l, n, _)| rel.ends_with(s) && *l == no && *n == needle)
{
used[i] = true;
} else {
violations.push(format!("{rel}:{no}: {why}: {}", line.trim()));
}
}
}
}
let stale: Vec<_> = ALLOW
.iter()
.zip(&used)
.filter(|(_, u)| !**u)
.map(|(a, _)| (a.0, a.1))
.collect();
assert!(
violations.is_empty(),
"hand-rolled 33-bit wrap arithmetic (use broadcast_common::clock33, or add a reasoned line-pinned ALLOW entry):\n{}",
violations.join("\n")
);
assert!(stale.is_empty(), "stale ALLOW entries: {stale:?}");
}
#[test]
fn scanner_catches_banned_shapes_and_skips_test_items() {
let src = "fn a(x: u64, y: u64) -> u64 { x.abs_diff(y) }\n\
const W: u64 = 1u64 << 33;\n\
const H: u64 = 0x2_0000_0000;\n\
const D: u64 = 8_589_934_592;\n\
#[cfg(test)]\nmod tests {\n const W: u64 = 1 << 33;\n}\n\
// 1 << 33 in a comment\n";
let hits: Vec<_> = non_test_lines(src)
.into_iter()
.flat_map(|(_, l)| banned_in_line(&l))
.collect();
assert_eq!(hits.len(), 4, "{hits:?}");
}