use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, fs};
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be set"));
fs::write(out_dir.join("lint_errors.rs"), "").expect("failed to write lint_errors.rs");
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock must be after the Unix epoch")
.as_secs();
println!("cargo:rustc-env=GAM_BUILD_TIMESTAMP={timestamp}");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/terms/penalties");
let manifest_dir =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set"));
emit_python_penalty_manifest(&manifest_dir)
.expect("failed to emit Python analytic-penalty manifest");
let needle: &str = concat!("TO", "DO");
let mut todo_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_banned_marker(&manifest_dir, &manifest_dir, needle, &mut todo_offenders);
let mut allow_offenders: Vec<(PathBuf, usize, String, String)> = Vec::new();
scan_for_banned_allow(&manifest_dir, &manifest_dir, &mut allow_offenders);
let mut underscore_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_let_underscore(&manifest_dir, &manifest_dir, &mut underscore_offenders);
let mut ignored: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_ignored_tests(&manifest_dir, &manifest_dir, &mut ignored);
let mut substring_offenders: Vec<(PathBuf, usize, &'static str, String)> = Vec::new();
scan_for_banned_substrings(&manifest_dir, &manifest_dir, &mut substring_offenders);
let mut unsafe_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_unsafe_without_safety(&manifest_dir, &manifest_dir, &mut unsafe_offenders);
let mut transmute_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_transmute_without_safety(&manifest_dir, &manifest_dir, &mut transmute_offenders);
let mut panic_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_panic_without_safety(&manifest_dir, &manifest_dir, &mut panic_offenders);
let mut dead_cfg_gate_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_dead_cfg_gates(&manifest_dir, &manifest_dir, &mut dead_cfg_gate_offenders);
let mut feature_cfg_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_feature_cfg_gates(&manifest_dir, &manifest_dir, &mut feature_cfg_offenders);
let mut cargo_feature_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_cargo_feature_entries(&manifest_dir, &manifest_dir, &mut cargo_feature_offenders);
let mut cargo_lint_allow_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_cargo_lint_allows(
&manifest_dir,
&manifest_dir,
&mut cargo_lint_allow_offenders,
);
let mut should_panic_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_bare_should_panic(&manifest_dir, &manifest_dir, &mut should_panic_offenders);
let mut debug_eprintln_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_debug_eprintln(&manifest_dir, &manifest_dir, &mut debug_eprintln_offenders);
let mut dead_guard_const_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_dead_guard_consts(
&manifest_dir,
&manifest_dir,
&mut dead_guard_const_offenders,
);
let mut underscore_fn_arg_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_underscore_fn_args(
&manifest_dir,
&manifest_dir,
&mut underscore_fn_arg_offenders,
);
let mut useless_test_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_useless_tests(&manifest_dir, &manifest_dir, &mut useless_test_offenders);
let mut cfg_test_pub_offenders: Vec<(PathBuf, usize, String, String)> = Vec::new();
scan_for_cfg_test_on_pub_items(&manifest_dir, &manifest_dir, &mut cfg_test_pub_offenders);
let mut noop_self_consuming_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_noop_self_consuming_fns(
&manifest_dir,
&manifest_dir,
&mut noop_self_consuming_offenders,
);
let mut stub_body_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_stub_function_bodies(&manifest_dir, &manifest_dir, &mut stub_body_offenders);
let mut dodge_name_offenders: Vec<(PathBuf, usize, String, String)> = Vec::new();
scan_for_dodge_named_fns(&manifest_dir, &manifest_dir, &mut dodge_name_offenders);
let mut vendor_offenders: Vec<(PathBuf, usize, String)> = Vec::new();
scan_for_vendor_directories(&manifest_dir, &manifest_dir, &mut vendor_offenders);
let mut history_violations: Vec<(PathBuf, usize, String, String)> = Vec::new();
run_unimplemented_history_audit(&manifest_dir, &mut history_violations);
let mut src_test_only_offenders: Vec<(PathBuf, usize, String, String)> = Vec::new();
let mut src_unreferenced_pub_scoped: Vec<(PathBuf, usize, String, String)> = Vec::new();
scan_for_src_items_used_only_by_tests(
&manifest_dir,
&mut src_test_only_offenders,
&mut src_unreferenced_pub_scoped,
);
let mut sections: Vec<Section> = Vec::new();
if !todo_offenders.is_empty() {
sections.push(Section {
title: format!("{} marker", needle),
rows: todo_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !allow_offenders.is_empty() {
sections.push(Section {
title: "#[allow(...)] / #[expect(...)] (any lint, anywhere — fix the underlying code instead of silencing the lint)".to_string(),
rows: allow_offenders
.iter()
.map(|(r, l, lint, s)| (r.clone(), *l, Some(lint.clone()), s.clone()))
.collect(),
});
}
if !underscore_offenders.is_empty() {
sections.push(Section {
title: "let _ binding (bare `_`, `_name`, or all-underscore tuple pattern)".to_string(),
rows: underscore_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !ignored.is_empty() {
sections.push(Section {
title: "#[ignore] test".to_string(),
rows: ignored
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !substring_offenders.is_empty() {
let mut by_label: std::collections::BTreeMap<&'static str, Vec<(PathBuf, usize, String)>> =
std::collections::BTreeMap::new();
for (rel, line_no, label, line) in &substring_offenders {
by_label
.entry(*label)
.or_default()
.push((rel.clone(), *line_no, line.clone()));
}
for (label, rows_in) in by_label {
sections.push(Section {
title: label.to_string(),
rows: rows_in
.into_iter()
.map(|(r, l, s)| (r, l, None, s))
.collect(),
});
}
}
if !unsafe_offenders.is_empty() {
sections.push(Section {
title: "unsafe without `// SAFETY:`".to_string(),
rows: unsafe_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !transmute_offenders.is_empty() {
sections.push(Section {
title: "mem::transmute without `// SAFETY:` justification".to_string(),
rows: transmute_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !panic_offenders.is_empty() {
sections.push(Section {
title: "panic!() in non-test code without `// SAFETY:` justification".to_string(),
rows: panic_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !dead_cfg_gate_offenders.is_empty() {
sections.push(Section {
title: "#[cfg(any())]/#[cfg(all())] (empty-arg cfg — permanently false/true)"
.to_string(),
rows: dead_cfg_gate_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !feature_cfg_offenders.is_empty() {
sections.push(Section {
title: "#[cfg(feature = ...)] / #[cfg_attr(feature = ...)] (feature gating banned — autoderive paths from problem characteristics; do not branch the codebase on opt-in flags)".to_string(),
rows: feature_cfg_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !cargo_lint_allow_offenders.is_empty() {
sections.push(Section {
title: "Cargo.toml [lints.*] `allow` entry (manifest-level lint silencing banned — fix the code instead of disabling the lint)".to_string(),
rows: cargo_lint_allow_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !cargo_feature_offenders.is_empty() {
sections.push(Section {
title: "Cargo.toml [features] entry (feature definitions banned — delete the [features] section and the corresponding cfg-gates)".to_string(),
rows: cargo_feature_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !should_panic_offenders.is_empty() {
sections.push(Section {
title: "#[should_panic] without `expected = \"...\"`".to_string(),
rows: should_panic_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !debug_eprintln_offenders.is_empty() {
sections.push(Section {
title: "eprintln!/eprint! with {:?} debug formatting (use real logging or delete)"
.to_string(),
rows: debug_eprintln_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !dead_guard_const_offenders.is_empty() {
sections.push(Section {
title:
"const <name>: bool = <literal> (dead-by-construction guard — use cfg or delete)"
.to_string(),
rows: dead_guard_const_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !underscore_fn_arg_offenders.is_empty() {
sections.push(Section {
title:
"underscore-prefixed fn parameter (use the value, restructure the API, or delete the param)"
.to_string(),
rows: underscore_fn_arg_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !useless_test_offenders.is_empty() {
sections.push(Section {
title:
"#[test] function without assertions (test must verify something — add assert! / assert_eq! / ? / #[should_panic] or delete the test)"
.to_string(),
rows: useless_test_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !cfg_test_pub_offenders.is_empty() {
sections.push(Section {
title:
"#[cfg(test)] on src/ item (move into a private `#[cfg(test)] mod tests { ... }` / `mod test_support` / `mod tests_*` / `mod *_tests`, or delete the unused item — `#[cfg(test)]` is not a dead_code-lint escape hatch, regardless of visibility)"
.to_string(),
rows: cfg_test_pub_offenders
.iter()
.map(|(r, l, item, s)| (r.clone(), *l, Some(item.clone()), s.clone()))
.collect(),
});
}
if !noop_self_consuming_offenders.is_empty() {
sections.push(Section {
title:
"no-op self-consuming fn (empty body with by-value `self` — likely a `let _` launderer; use or restructure the return value)"
.to_string(),
rows: noop_self_consuming_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !stub_body_offenders.is_empty() {
sections.push(Section {
title:
"stub function body (multi-arg function whose entire body is a sentinel like None/Ok(())/Default::default() — implement the function, return a real Result/Error, or delete the function)"
.to_string(),
rows: stub_body_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !dodge_name_offenders.is_empty() {
sections.push(Section {
title:
"dodge-named function (name announces lint-laundering intent — implement real behavior or restructure so the value isn't unused)"
.to_string(),
rows: dodge_name_offenders
.iter()
.map(|(r, l, ident, s)| (r.clone(), *l, Some(ident.clone()), s.clone()))
.collect(),
});
}
if !vendor_offenders.is_empty() {
sections.push(Section {
title:
"`vendor/` directory present — vendoring forks upstream dependencies past the same lint gate this scanner enforces; use `[dependencies]` in Cargo.toml (crates.io version or `git = ...`) instead"
.to_string(),
rows: vendor_offenders
.iter()
.map(|(r, l, s)| (r.clone(), *l, None, s.clone()))
.collect(),
});
}
if !history_violations.is_empty() {
sections.push(Section {
title: "unimplemented!/todo!/unreachable! removed without real implementation \
(history audit — ban_history.txt)"
.to_string(),
rows: history_violations
.iter()
.map(|(file, line, sig, reason)| {
(file.clone(), *line, Some(reason.clone()), sig.clone())
})
.collect(),
});
}
if !src_test_only_offenders.is_empty() {
sections.push(Section {
title:
"src/ item referenced only by tests (production code with no production consumers — implement a real caller, delete the item, or move it into a `#[cfg(test)]` private mod if it's genuinely test-support)"
.to_string(),
rows: src_test_only_offenders
.iter()
.map(|(r, l, hint, s)| (r.clone(), *l, Some(hint.clone()), s.clone()))
.collect(),
});
}
if !src_unreferenced_pub_scoped.is_empty() {
sections.push(Section {
title:
"pub(crate)/pub(super) item in src/ with ZERO consumers anywhere (no production caller, no test reference — implement a real caller or delete the item; rustc's `dead_code` lint cannot see this in a library-shaped crate)"
.to_string(),
rows: src_unreferenced_pub_scoped
.iter()
.map(|(r, l, hint, s)| (r.clone(), *l, Some(hint.clone()), s.clone()))
.collect(),
});
}
if sections.is_empty() {
return;
}
render_report(§ions);
std::process::exit(1);
}
#[derive(Clone)]
struct PenaltyWrapperManifest {
kind_tag: String,
rust_type: String,
python_wrapper: String,
row_block_diagonal: bool,
}
fn emit_python_penalty_manifest(manifest_dir: &Path) -> std::io::Result<()> {
let penalties_dir = manifest_dir.join("src").join("terms").join("penalties");
let registry = fs::read_to_string(penalties_dir.join("mod.rs"))?;
let mut wrappers = Vec::new();
for line in registry.lines() {
let trimmed = line.trim();
if !trimmed.starts_with("register!(") {
continue;
}
let inside = trimmed
.trim_start_matches("register!(")
.trim_end_matches(");");
let mut pieces = inside.split(',').map(str::trim);
let variant = match pieces.next() {
Some(value) if !value.is_empty() => value,
_ => continue,
};
let rust_type = match pieces.next() {
Some(value) if !value.is_empty() => value,
_ => continue,
};
let source = penalty_manifest_source_for_type(&penalties_dir, rust_type)?;
wrappers.push(PenaltyWrapperManifest {
kind_tag: manifest_const_string(&source, "KIND_TAG")?,
rust_type: format!("{variant}:{rust_type}"),
python_wrapper: manifest_const_string(&source, "PYTHON_WRAPPER")?,
row_block_diagonal: manifest_const_bool(&source, "ROW_BLOCK_DIAGONAL")?,
});
}
let mut output = String::from(
"# Generated by build.rs from src/terms/penalties manifests.\n\
PENALTY_MANIFEST = (\n",
);
for wrapper in wrappers {
output.push_str(" {\n");
output.push_str(&format!(" \"kind\": {:?},\n", wrapper.kind_tag));
output.push_str(&format!(" \"rust\": {:?},\n", wrapper.rust_type));
output.push_str(&format!(
" \"python\": {:?},\n",
wrapper.python_wrapper
));
output.push_str(&format!(
" \"row_block_diagonal\": {},\n",
if wrapper.row_block_diagonal {
"True"
} else {
"False"
}
));
output.push_str(" },\n");
}
output.push_str(")\n");
fs::write(
manifest_dir.join("gamfit").join("_penalties_manifest.py"),
output,
)
}
fn penalty_manifest_source_for_type(
penalties_dir: &Path,
rust_type: &str,
) -> std::io::Result<String> {
for entry in fs::read_dir(penalties_dir)? {
let path = entry?.path();
if path.extension() != Some(OsStr::new("rs"))
|| path.file_name() == Some(OsStr::new("mod.rs"))
{
continue;
}
if path
.file_name()
.and_then(OsStr::to_str)
.is_some_and(|name| name.starts_with("._"))
{
continue;
}
let source = fs::read_to_string(&path)?;
if source.contains(&format!("impl PenaltyManifest for {rust_type}")) {
return Ok(source);
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("missing PenaltyManifest impl for {rust_type}"),
))
}
fn manifest_const_string(source: &str, key: &str) -> std::io::Result<String> {
let needle = format!("const {key}: &'static str = ");
for line in source.lines() {
let trimmed = line.trim();
if !trimmed.starts_with(&needle) {
continue;
}
let value = trimmed
.trim_start_matches(&needle)
.trim_end_matches(';')
.trim();
return Ok(value.trim_matches('"').to_string());
}
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("missing manifest const {key}"),
))
}
fn manifest_const_bool(source: &str, key: &str) -> std::io::Result<bool> {
let needle = format!("const {key}: bool = ");
for line in source.lines() {
let trimmed = line.trim();
if !trimmed.starts_with(&needle) {
continue;
}
let value = trimmed
.trim_start_matches(&needle)
.trim_end_matches(';')
.trim();
return match value {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid bool manifest const {key}: {value}"),
)),
};
}
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("missing manifest const {key}"),
))
}
struct Section {
title: String,
rows: Vec<(PathBuf, usize, Option<String>, String)>,
}
fn render_report(sections: &[Section]) {
let total: usize = sections.iter().map(|s| s.rows.len()).sum();
eprintln!();
eprintln!(
"error: {} ban violation{} across {} rule{}",
total,
if total == 1 { "" } else { "s" },
sections.len(),
if sections.len() == 1 { "" } else { "s" },
);
for section in sections {
eprintln!();
eprintln!(
"── {} ({} hit{})",
section.title,
section.rows.len(),
if section.rows.len() == 1 { "" } else { "s" },
);
for (rel, line_no, tag, line) in §ion.rows {
let trimmed = line.trim();
let snippet: String = trimmed.chars().take(160).collect();
match tag {
Some(t) => eprintln!(" {}:{}: [{}] {}", rel.display(), line_no, t, snippet),
None => eprintln!(" {}:{}: {}", rel.display(), line_no, snippet),
}
}
}
eprintln!();
eprintln!("summary:");
for section in sections {
eprintln!(" {:>5} {}", section.rows.len(), section.title);
}
eprintln!();
}
fn banned_substrings() -> &'static [(&'static str, &'static str, bool)] {
&[
("dbg!(", "dbg!", true),
("todo!(", "todo!", true),
("unimplemented!(", "unimplemented!", true),
("unreachable!(", "unreachable!", true),
("assert!(true)", "assert!(true)", false),
("assert!(false)", "assert!(false)", false),
("debug_assert!(", "debug_assert!", false),
("debug_assert_eq!(", "debug_assert_eq!", false),
("debug_assert_ne!(", "debug_assert_ne!", false),
("hint::black_box(", "hint::black_box", true),
("std::hint::black_box(", "std::hint::black_box", true),
("core::hint::black_box(", "core::hint::black_box", true),
("cfg!(debug_assertions)", "cfg!(debug_assertions)", true),
("cfg!(test)", "cfg!(test)", true),
("Arc::strong_count(", "Arc::strong_count", true),
("Arc::weak_count(", "Arc::weak_count", true),
("Rc::strong_count(", "Rc::strong_count", true),
("Rc::weak_count(", "Rc::weak_count", true),
(
"file!().ends_with(\".rs\")",
"file!().ends_with(\".rs\")",
false,
),
(
"file!().ends_with(\".rs\"",
"file!().ends_with(\".rs\"",
false,
),
("std::process::exit(", "std::process::exit", false),
("process::exit(", "process::exit", false),
("std::process::abort(", "std::process::abort", false),
("process::abort(", "process::abort", false),
("println!(", "println!", true),
("print!(", "print!", true),
("mem::forget(", "mem::forget", true),
("Box::leak(", "Box::leak", true),
("env::var(", "env::var", false),
("env::var_os(", "env::var_os", false),
("env::vars(", "env::vars", false),
("env::vars_os(", "env::vars_os", false),
("if let Ok(_)", "if let Ok(_)", false),
("if let Some(_)", "if let Some(_)", false),
("if let Err(_)", "if let Err(_)", false),
("== true", "== true", false),
("== false", "== false", false),
("!= true", "!= true", false),
("!= false", "!= false", false),
("thread::sleep(", "thread::sleep", true),
("std::thread::sleep(", "std::thread::sleep", true),
("spin_loop()", "spin_loop", false),
("thread::yield_now(", "thread::yield_now", false),
("std::thread::yield_now(", "std::thread::yield_now", false),
]
}
fn scan_for_banned_substrings(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, &'static str, String)>,
) {
let needles = banned_substrings();
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
let mask = compute_test_mask(content, rel);
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
let in_test = mask.get(idx).copied().unwrap_or(false);
for (needle, label, test_aware) in needles {
if *test_aware && in_test {
continue;
}
if stripped.contains(needle) {
offenders.push((rel.to_path_buf(), idx + 1, *label, line.to_string()));
}
}
}
});
}
fn compute_test_mask(content: &str, rel: &Path) -> Vec<bool> {
let lines: Vec<&str> = content.lines().collect();
let n = lines.len();
let mut mask = vec![false; n];
let rel_str = rel.to_string_lossy().replace('\\', "/");
let file_is_test = rel_str.starts_with("tests/")
|| rel_str.starts_with("bench/")
|| rel_str.starts_with("benches/")
|| path_matches_crates_test(&rel_str);
if file_is_test {
mask.fill(true);
return mask;
}
let mut depth: i32 = 0;
let mut pending_attr = false;
let mut gate_stack: Vec<i32> = Vec::new();
let stripped_all = strip_file_lines(content);
for (idx, _raw) in lines.iter().enumerate() {
let stripped = stripped_all.get(idx).cloned().unwrap_or_default();
if is_cfg_test_attr_line(&stripped) {
pending_attr = true;
}
let bytes = stripped.as_bytes();
let inside_at_line_start = !gate_stack.is_empty();
mask[idx] = inside_at_line_start;
for &b in bytes {
if b == b'{' {
depth += 1;
if pending_attr {
gate_stack.push(depth - 1);
pending_attr = false;
}
} else if b == b'}' {
if let Some(&entry) = gate_stack.last()
&& depth - 1 == entry
{
gate_stack.pop();
}
depth -= 1;
}
}
}
mask
}
fn path_matches_crates_test(rel: &str) -> bool {
let Some(rest) = rel.strip_prefix("crates/") else {
return false;
};
let Some(slash) = rest.find('/') else {
return false;
};
let tail = &rest[slash + 1..];
tail.starts_with("tests/") || tail.starts_with("benches/")
}
fn is_cfg_test_attr_line(stripped: &str) -> bool {
let bytes = stripped.as_bytes();
let mut i = 0usize;
while i + 1 < bytes.len() {
if bytes[i] == b'#' && bytes[i + 1] == b'[' {
let rest = &stripped[i + 2..];
if let Some(pos) = rest.find("cfg(") {
let abs = i + 2 + pos;
let before_ok = abs == 0 || !is_ident_byte(bytes[abs - 1]);
if before_ok {
let args_start = abs + 4; if let Some(end) = find_matching_paren(&bytes[args_start..]) {
let args = &stripped[args_start..args_start + end];
if cfg_args_contain_test(args) {
return true;
}
}
}
}
}
i += 1;
}
false
}
fn find_matching_paren(bytes: &[u8]) -> Option<usize> {
let mut depth: i32 = 1;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'(' => depth += 1,
b')' => {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
_ => {}
}
i += 1;
}
None
}
fn cfg_args_contain_test(args: &str) -> bool {
let bytes = args.as_bytes();
let kw = b"test";
let mut i = 0usize;
while i + kw.len() <= bytes.len() {
if &bytes[i..i + kw.len()] == kw {
let before_ok = i == 0 || !is_ident_byte(bytes[i - 1]);
let after_ok = i + kw.len() == bytes.len() || !is_ident_byte(bytes[i + kw.len()]);
if before_ok && after_ok {
return true;
}
}
i += 1;
}
false
}
fn scan_for_unsafe_without_safety(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("unsafe") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
for (idx, line) in lines.iter().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !line_has_keyword(stripped, "unsafe") {
continue;
}
if line.contains("SAFETY:") {
continue;
}
if is_unsafe_marker_impl(stripped) {
continue;
}
let mut justified = false;
let mut seen = 0usize;
let mut k = idx;
while k > 0 && seen < 10 {
k -= 1;
let prev = lines[k];
if prev.trim().is_empty() {
break;
}
seen += 1;
if prev.contains("SAFETY:") {
justified = true;
break;
}
}
if !justified {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn is_unsafe_marker_impl(stripped: &str) -> bool {
for marker in ["Send", "Sync"] {
let needle_owned = format!("unsafe impl {}", marker);
let needle = needle_owned.as_str();
let bytes = stripped.as_bytes();
let nb = needle.as_bytes();
let mut i = 0usize;
while i + nb.len() <= bytes.len() {
if &bytes[i..i + nb.len()] == nb {
let after = i + nb.len();
if after == bytes.len() {
return true;
}
let c = bytes[after];
if c == b'<' || c.is_ascii_whitespace() {
return true;
}
if after + 3 <= bytes.len() && &bytes[after..after + 3] == b"for" {
return true;
}
}
i += 1;
}
}
false
}
fn scan_for_transmute_without_safety(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("transmute") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
for (idx, line) in lines.iter().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !line_has_keyword(stripped, "transmute") {
continue;
}
if !stripped.contains("mem::") && !stripped.contains("transmute::<") {
continue;
}
if line.contains("SAFETY:") {
continue;
}
let mut justified = false;
let mut seen = 0usize;
let mut k = idx;
while k > 0 && seen < 10 {
k -= 1;
let prev = lines[k];
if prev.trim().is_empty() {
break;
}
seen += 1;
if prev.contains("SAFETY:") {
justified = true;
break;
}
}
if !justified {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_panic_without_safety(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("panic!(") {
return;
}
let mask = compute_test_mask(content, rel);
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
for (idx, line) in lines.iter().enumerate() {
if mask.get(idx).copied().unwrap_or(false) {
continue;
}
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !stripped.contains("panic!(") {
continue;
}
if line.contains("SAFETY:") {
continue;
}
let mut justified = false;
let mut seen = 0usize;
let mut k = idx;
while k > 0 && seen < 10 {
k -= 1;
let prev = lines[k];
if prev.trim().is_empty() {
break;
}
seen += 1;
if prev.contains("SAFETY:") {
justified = true;
break;
}
}
if !justified {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_dead_cfg_gates(root: &Path, dir: &Path, offenders: &mut Vec<(PathBuf, usize, String)>) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("cfg(") {
return;
}
for (idx, line) in content.lines().enumerate() {
let stripped = strip_strings_and_comments(line);
if !stripped.contains("#[") {
continue;
}
if line_has_empty_cfg_gate(&stripped) {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_feature_cfg_gates(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("feature") {
return;
}
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !(stripped.contains("#[") || stripped.contains("#![")) {
continue;
}
if !(stripped.contains("cfg(") || stripped.contains("cfg_attr(")) {
continue;
}
if line_has_feature_predicate(stripped) && !line_is_cuda_feature_gate(stripped) {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn line_has_feature_predicate(stripped: &str) -> bool {
let bytes = stripped.as_bytes();
let kw = b"feature";
let mut i = 0usize;
while i + kw.len() <= bytes.len() {
if &bytes[i..i + kw.len()] == kw {
let before_ok = i == 0 || !is_ident_byte(bytes[i - 1]);
let after_pos = i + kw.len();
let after_ok = after_pos == bytes.len() || !is_ident_byte(bytes[after_pos]);
if before_ok && after_ok {
let mut j = after_pos;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j < bytes.len() && bytes[j] == b'=' {
return true;
}
}
}
i += 1;
}
false
}
fn line_is_cuda_feature_gate(stripped: &str) -> bool {
let mut rest = stripped;
let mut saw_cuda = false;
while let Some(pos) = rest.find("feature") {
let after = &rest[pos + "feature".len()..];
let Some(eq_pos) = after.find('=') else {
return false;
};
let after_eq = after[eq_pos + 1..].trim_start();
if !after_eq.starts_with("\"cuda\"") {
return false;
}
saw_cuda = true;
rest = &after_eq["\"cuda\"".len()..];
}
saw_cuda
}
fn scan_for_cargo_feature_entries(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let basename = rel.file_name().and_then(OsStr::to_str).unwrap_or("");
if basename != "Cargo.toml" {
return;
}
let mut in_features = false;
for (idx, line) in content.lines().enumerate() {
let trimmed = line.trim();
let code_part = match trimmed.find('#') {
Some(p) => trimmed[..p].trim_end(),
None => trimmed,
};
if code_part.starts_with('[') && code_part.ends_with(']') {
let header = code_part
.trim_start_matches('[')
.trim_end_matches(']')
.trim();
in_features = header == "features"
|| header.starts_with("features.")
|| header == "workspace.features"
|| header.starts_with("workspace.features.");
continue;
}
if !in_features {
continue;
}
if code_part.is_empty() {
continue;
}
let bytes = code_part.as_bytes();
let mut j = 0usize;
while j < bytes.len()
&& (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_' || bytes[j] == b'-')
{
j += 1;
}
if j == 0 {
continue;
}
let mut k = j;
while k < bytes.len() && bytes[k].is_ascii_whitespace() {
k += 1;
}
if k < bytes.len()
&& bytes[k] == b'='
&& code_part != "default = []"
&& !code_part.starts_with("cuda = ")
{
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_cargo_lint_allows(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let basename = rel.file_name().and_then(OsStr::to_str).unwrap_or("");
if basename != "Cargo.toml" {
return;
}
let mut in_lints = false;
let mut in_clippy_section = false;
for (idx, line) in content.lines().enumerate() {
let trimmed = line.trim();
let code_part = match trimmed.find('#') {
Some(p) => trimmed[..p].trim_end(),
None => trimmed,
};
if code_part.starts_with('[') && code_part.ends_with(']') {
let header = code_part
.trim_start_matches('[')
.trim_end_matches(']')
.trim();
in_lints = header == "lints"
|| header.starts_with("lints.")
|| header == "workspace.lints"
|| header.starts_with("workspace.lints.");
in_clippy_section = header == "lints.clippy" || header == "workspace.lints.clippy";
continue;
}
if !in_lints || code_part.is_empty() {
continue;
}
if in_clippy_section
|| code_part.starts_with("clippy.")
|| code_part.starts_with("\"clippy::")
{
continue;
}
if code_part.contains("\"allow\"") {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_cfg_test_on_pub_items(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
let in_src = rel_str.starts_with("src/")
|| rel_str
.strip_prefix("crates/")
.and_then(|rest| rest.find('/').map(|i| &rest[i + 1..]))
.is_some_and(|tail| tail.starts_with("src/"));
if !in_src {
return;
}
if !content.contains("cfg(") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
let n = lines.len();
for idx in 0..n {
let stripped = stripped_lines
.get(idx)
.map(String::as_str)
.unwrap_or(lines[idx]);
if !is_cfg_test_attr_line(stripped) {
continue;
}
let mut j = idx + 1;
let mut item_line: Option<usize> = None;
while j < n {
let sj = stripped_lines
.get(j)
.map(String::as_str)
.unwrap_or(lines[j]);
let t = sj.trim();
if t.is_empty() {
j += 1;
continue;
}
if t.starts_with("//") {
j += 1;
continue;
}
if t.starts_with("#[") || t.starts_with("#![") {
j += 1;
continue;
}
item_line = Some(j);
break;
}
let Some(item_idx) = item_line else {
continue;
};
let item_raw = lines.get(item_idx).copied().unwrap_or("");
let item_trim = item_raw.trim_start();
if let Some(rest) = item_trim.strip_prefix("mod ") {
let name: String = rest
.chars()
.take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
.collect();
if !name.is_empty() && is_exempt_test_submodule_name(&name) {
continue;
}
}
let mut descriptor: String = item_trim
.chars()
.take_while(|c| *c != '{' && *c != ';' && *c != '\n')
.collect();
descriptor = descriptor.trim().chars().take(80).collect();
offenders.push((
rel.to_path_buf(),
idx + 1,
descriptor,
lines[idx].to_string(),
));
}
});
}
fn is_exempt_test_submodule_name(name: &str) -> bool {
name == "tests"
|| name == "test_support"
|| name.starts_with("tests_")
|| name.ends_with("_tests")
}
fn line_has_empty_cfg_gate(stripped: &str) -> bool {
for marker in ["cfg(any(", "cfg(all("] {
let mut search_from = 0usize;
while let Some(rel_pos) = stripped[search_from..].find(marker) {
let inner_start = search_from + rel_pos + marker.len();
let bytes = stripped.as_bytes();
let mut j = inner_start;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j < bytes.len() && bytes[j] == b')' {
return true;
}
search_from = inner_start;
}
}
false
}
fn scan_for_bare_should_panic(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("#[should_panic") {
return;
}
for (idx, line) in content.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("#[should_panic]")
|| (trimmed.starts_with("#[should_panic") && !trimmed.contains("expected"))
{
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_dead_guard_consts(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("const ") {
return;
}
let trait_impl_mask = compute_trait_impl_mask(content);
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !line_has_keyword(stripped, "const") {
continue;
}
if line_matches_bool_literal_const(stripped) {
if trait_impl_mask.get(idx).copied().unwrap_or(false) {
continue;
}
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn compute_trait_impl_mask(content: &str) -> Vec<bool> {
let lines: Vec<&str> = content.lines().collect();
let n = lines.len();
let mut mask = vec![false; n];
let stripped_lines: Vec<String> = strip_file_lines(content);
let mut stack: Vec<(i32, bool)> = Vec::new();
let mut depth: i32 = 0;
let mut pending_impl = false;
let mut pending_is_trait = false;
for (idx, stripped) in stripped_lines.iter().enumerate() {
let inside_trait_impl = stack.iter().any(|(_, t)| *t);
mask[idx] = inside_trait_impl;
let trimmed = stripped.trim_start();
let starts_impl = trimmed.starts_with("impl ")
|| trimmed.starts_with("impl<")
|| trimmed.starts_with("unsafe impl ")
|| trimmed.starts_with("unsafe impl<")
|| trimmed.starts_with("default impl ")
|| trimmed.starts_with("default impl<");
if starts_impl && depth == 0 {
pending_impl = true;
pending_is_trait = stripped.contains(" for ");
} else if pending_impl && stripped.contains(" for ") {
pending_is_trait = true;
}
let bytes = stripped.as_bytes();
for &b in bytes {
if b == b'{' {
let opened_at_depth = depth;
depth += 1;
if pending_impl && opened_at_depth == 0 {
stack.push((opened_at_depth, pending_is_trait));
pending_impl = false;
pending_is_trait = false;
} else {
stack.push((opened_at_depth, false));
}
} else if b == b'}' {
if let Some(&(entry, _)) = stack.last()
&& depth - 1 == entry
{
stack.pop();
}
depth -= 1;
}
}
}
mask
}
fn line_matches_bool_literal_const(stripped: &str) -> bool {
let bytes = stripped.as_bytes();
let kw = b"const";
let mut i = 0usize;
'outer: while i + kw.len() <= bytes.len() {
if &bytes[i..i + kw.len()] != kw {
i += 1;
continue;
}
let before_ok = i == 0 || !is_ident_byte(bytes[i - 1]);
let after_ok = i + kw.len() < bytes.len() && !is_ident_byte(bytes[i + kw.len()]);
if !before_ok || !after_ok {
i += 1;
continue;
}
let mut j = i + kw.len();
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || !(bytes[j].is_ascii_alphabetic() || bytes[j] == b'_') {
i += 1;
continue;
}
while j < bytes.len() && is_ident_byte(bytes[j]) {
j += 1;
}
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || bytes[j] != b':' {
i += 1;
continue;
}
j += 1;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
let bool_kw = b"bool";
if j + bool_kw.len() > bytes.len() || &bytes[j..j + bool_kw.len()] != bool_kw {
i += 1;
continue;
}
let after = j + bool_kw.len();
if after < bytes.len() && is_ident_byte(bytes[after]) {
i += 1;
continue;
}
j = after;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || bytes[j] != b'=' {
i += 1;
continue;
}
j += 1;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
for lit in [b"true".as_ref(), b"false".as_ref()] {
if j + lit.len() <= bytes.len() && &bytes[j..j + lit.len()] == lit {
let after_lit = j + lit.len();
let bound_ok = after_lit == bytes.len() || !is_ident_byte(bytes[after_lit]);
if bound_ok {
let mut k = after_lit;
while k < bytes.len() && bytes[k].is_ascii_whitespace() {
k += 1;
}
if k < bytes.len() && bytes[k] == b';' {
return true;
}
}
}
}
i += 1;
continue 'outer;
}
false
}
fn scan_for_debug_eprintln(root: &Path, dir: &Path, offenders: &mut Vec<(PathBuf, usize, String)>) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("eprint") {
return;
}
let mask = compute_test_mask(content, rel);
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
if mask.get(idx).copied().unwrap_or(false) {
continue;
}
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !stripped.contains("eprintln!(") && !stripped.contains("eprint!(") {
continue;
}
if line.contains(":?}") || line.contains(":#?}") {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn strip_strings_and_comments(line: &str) -> String {
strip_strings_and_comments_stateful(line, false, 0).0
}
fn strip_file_lines(content: &str) -> Vec<String> {
let mut out: Vec<String> = Vec::new();
let mut in_str = false;
let mut quote: u8 = 0;
let mut raw_hashes: u8 = 0;
for line in content.lines() {
let (stripped, after_in_str, after_quote, after_hashes) =
strip_strings_and_comments_stateful_raw(line, in_str, quote, raw_hashes);
out.push(stripped);
in_str = after_in_str;
quote = after_quote;
raw_hashes = after_hashes;
}
out
}
fn strip_strings_and_comments_stateful(
line: &str,
in_str_in: bool,
quote_in: u8,
) -> (String, bool, u8) {
let result = strip_strings_and_comments_stateful_raw(line, in_str_in, quote_in, 0);
(result.0, result.1, result.2)
}
fn strip_strings_and_comments_stateful_raw(
line: &str,
in_str_in: bool,
quote_in: u8,
hashes_in: u8,
) -> (String, bool, u8, u8) {
let bytes = line.as_bytes();
let mut out = Vec::with_capacity(bytes.len());
let mut i = 0usize;
let mut in_str = in_str_in;
let mut str_quote: u8 = quote_in;
let mut raw_hashes: u8 = hashes_in;
while i < bytes.len() {
let c = bytes[i];
if in_str && str_quote == b'r' {
if c == b'"' {
let need = raw_hashes as usize;
let mut k = i + 1;
let mut count = 0usize;
while k < bytes.len() && bytes[k] == b'#' && count < need {
k += 1;
count += 1;
}
if count == need {
in_str = false;
str_quote = 0;
raw_hashes = 0;
out.push(b'"');
for _ in 0..need {
out.push(b'#');
}
i = k;
continue;
}
}
out.push(b' ');
i += 1;
continue;
}
if in_str {
if c == b'\\' && i + 1 < bytes.len() {
out.push(b' ');
out.push(b' ');
i += 2;
continue;
}
if c == str_quote {
in_str = false;
out.push(c);
} else {
out.push(b' ');
}
i += 1;
continue;
}
if c == b'/' && i + 1 < bytes.len() && bytes[i + 1] == b'/' {
break;
}
let prev_is_ident = i > 0 && (bytes[i - 1].is_ascii_alphanumeric() || bytes[i - 1] == b'_');
if !prev_is_ident
&& (c == b'r' || (c == b'b' && i + 1 < bytes.len() && bytes[i + 1] == b'r'))
{
let prefix_len = if c == b'b' { 2usize } else { 1usize };
let mut k = i + prefix_len;
let mut hashes = 0usize;
while k < bytes.len() && bytes[k] == b'#' {
k += 1;
hashes += 1;
}
if k < bytes.len() && bytes[k] == b'"' && hashes <= u8::MAX as usize {
in_str = true;
str_quote = b'r';
raw_hashes = hashes as u8;
for j in i..=k {
out.push(bytes[j]);
}
i = k + 1;
continue;
}
}
if c == b'"' {
in_str = true;
str_quote = c;
out.push(c);
i += 1;
continue;
}
if c == b'\'' {
let is_char_lit = if i + 1 < bytes.len() && bytes[i + 1] == b'\\' {
let mut k = i + 2;
let mut found = false;
while k < bytes.len() && k < i + 10 {
if bytes[k] == b'\'' {
found = true;
break;
}
k += 1;
}
found
} else if i + 2 < bytes.len() && bytes[i + 1] != b'\'' && bytes[i + 2] == b'\'' {
true
} else if i + 3 < bytes.len() && bytes[i + 1] != b'\'' && bytes[i + 3] == b'\'' {
true
} else {
false
};
if is_char_lit {
in_str = true;
str_quote = c;
out.push(c);
i += 1;
continue;
}
out.push(c);
i += 1;
continue;
}
out.push(c);
i += 1;
}
let s = String::from_utf8(out).unwrap_or_else(|_| line.to_string());
(s, in_str, str_quote, raw_hashes)
}
fn line_has_keyword(line: &str, kw: &str) -> bool {
let bytes = line.as_bytes();
let kw_bytes = kw.as_bytes();
if kw_bytes.is_empty() || bytes.len() < kw_bytes.len() {
return false;
}
let mut i = 0usize;
while i + kw_bytes.len() <= bytes.len() {
if &bytes[i..i + kw_bytes.len()] == kw_bytes {
let before_ok = i == 0 || !is_ident_byte(bytes[i - 1]);
let after_ok =
i + kw_bytes.len() == bytes.len() || !is_ident_byte(bytes[i + kw_bytes.len()]);
if before_ok && after_ok {
return true;
}
}
i += 1;
}
false
}
fn scan_for_banned_marker(
root: &Path,
dir: &Path,
needle: &str,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
if !content.contains(needle) {
return;
}
let use_strip = rel.extension().and_then(OsStr::to_str) == Some("rs");
let stripped_lines = if use_strip {
Some(strip_file_lines(content))
} else {
None
};
for (idx, line) in content.lines().enumerate() {
let probe: &str = match &stripped_lines {
Some(v) => v.get(idx).map(String::as_str).unwrap_or(line),
None => line,
};
if probe.contains(needle) {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_banned_allow(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String, String)>,
) {
const SILENCERS: &[&str] = &["allow(", "expect("];
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !SILENCERS.iter().any(|s| content.contains(s)) {
return;
}
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
let code = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !code.contains("#[") {
continue;
}
for silencer in SILENCERS {
let mut search_from = 0usize;
while let Some(rel_idx) = code[search_from..].find(silencer) {
let abs_match = search_from + rel_idx;
let mut k = abs_match;
while k > 0 && code.as_bytes()[k - 1].is_ascii_whitespace() {
k -= 1;
}
let prefix = &code[..k];
let is_attr = prefix.ends_with("#[") || prefix.ends_with("#![");
let start = abs_match + silencer.len();
if !is_attr {
search_from = start;
continue;
}
let Some(end_rel) = code[start..].find(')') else {
break;
};
let inside = &code[start..start + end_rel];
let mut first_label: Option<String> = None;
let mut any_non_clippy = false;
for tok in inside.split(',') {
let trimmed = tok.trim();
if trimmed.is_empty() {
continue;
}
if trimmed.starts_with("clippy::") {
continue;
}
any_non_clippy = true;
if first_label.is_none() {
let bare = trimmed
.trim_start_matches("rustc::")
.trim_start_matches("rustdoc::");
first_label = Some(bare.to_string());
}
}
if any_non_clippy {
let attr = silencer.trim_end_matches('(');
let label = first_label.unwrap_or_else(|| "<empty>".to_string());
offenders.push((
rel.to_path_buf(),
idx + 1,
format!("{attr}({label})"),
line.to_string(),
));
}
search_from = start + end_rel + 1;
if search_from >= code.len() {
break;
}
}
}
}
});
}
fn scan_for_let_underscore(root: &Path, dir: &Path, offenders: &mut Vec<(PathBuf, usize, String)>) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("let ") {
return;
}
let mask = compute_test_mask(content, rel);
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
if mask.get(idx).copied().unwrap_or(false) {
continue;
}
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if stripped_line_has_let_underscore(stripped) {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn stripped_line_has_let_underscore(line: &str) -> bool {
let bytes = line.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
if i + 3 < bytes.len()
&& &bytes[i..i + 3] == b"let"
&& (i == 0 || !is_ident_byte(bytes[i - 1]))
&& bytes[i + 3].is_ascii_whitespace()
{
let mut j = i + 3;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j + 3 <= bytes.len()
&& &bytes[j..j + 3] == b"mut"
&& j + 3 < bytes.len()
&& bytes[j + 3].is_ascii_whitespace()
{
j += 3;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
}
if j < bytes.len() && bytes[j] == b'_' {
return true;
}
if j < bytes.len()
&& bytes[j] == b'('
&& tuple_pattern_all_underscore(bytes, j) == Some(true)
{
return true;
}
i = j;
continue;
}
i += 1;
}
false
}
fn tuple_pattern_all_underscore(bytes: &[u8], start: usize) -> Option<bool> {
if start >= bytes.len() || bytes[start] != b'(' {
return None;
}
let mut depth = 0i32;
let mut k = start;
let mut any_binding = false;
let mut all_underscore = true;
while k < bytes.len() {
let b = bytes[k];
match b {
b'(' => {
depth += 1;
k += 1;
}
b')' => {
depth -= 1;
k += 1;
if depth == 0 {
if !any_binding {
return None;
}
return Some(all_underscore);
}
}
b',' | b' ' | b'\t' => {
k += 1;
}
b'=' => {
return None;
}
_ => {
if b == b'_' || b.is_ascii_alphabetic() {
let s = k;
while k < bytes.len() && is_ident_byte(bytes[k]) {
k += 1;
}
let word = &bytes[s..k];
if word == b"mut" || word == b"ref" {
continue;
}
let mut m = k;
while m < bytes.len() && (bytes[m] == b' ' || bytes[m] == b'\t') {
m += 1;
}
if m < bytes.len() {
let nb = bytes[m];
if nb == b'(' || nb == b'{' {
return None;
}
if nb == b':' && m + 1 < bytes.len() && bytes[m + 1] == b':' {
return None;
}
}
any_binding = true;
if !word.starts_with(b"_") {
all_underscore = false;
}
} else {
return None;
}
}
}
}
None
}
fn is_ident_byte(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'_'
}
fn scan_for_ignored_tests(root: &Path, dir: &Path, offenders: &mut Vec<(PathBuf, usize, String)>) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("#[ignore") {
return;
}
for (idx, line) in content.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("#[ignore]") || trimmed.starts_with("#[ignore =") {
offenders.push((rel.to_path_buf(), idx + 1, line.to_string()));
}
}
});
}
fn scan_for_vendor_directories(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
let read = match fs::read_dir(dir) {
Ok(r) => r,
Err(_) => return,
};
for entry in read.flatten() {
let path = entry.path();
let name = path.file_name().and_then(OsStr::to_str).unwrap_or("");
if name.starts_with('.')
|| name == "target"
|| name.starts_with("target-")
|| name == "node_modules"
|| name == "__pycache__"
|| name == "pydeps"
|| name == "site-packages"
|| name == "venv"
|| name == "dist"
|| name == "build"
|| name == "site"
{
continue;
}
if !path.is_dir() {
continue;
}
if name == "vendor" {
let rel = path.strip_prefix(root).unwrap_or(&path).to_path_buf();
offenders.push((
rel,
1,
"vendored crates are forbidden; depend on crates.io or `git = \"…\"` in Cargo.toml"
.to_string(),
));
continue;
}
scan_for_vendor_directories(root, &path, offenders);
}
}
fn visit_files(root: &Path, dir: &Path, visitor: &mut dyn FnMut(&Path, &str)) {
let read = match fs::read_dir(dir) {
Ok(r) => r,
Err(_) => return,
};
for entry in read.flatten() {
let path = entry.path();
let name = path.file_name().and_then(OsStr::to_str).unwrap_or("");
if path
.strip_prefix(root)
.ok()
.is_some_and(|rel| rel.starts_with("bench/runtime/pydeps"))
{
continue;
}
if name.starts_with('.')
|| name == "target"
|| name.starts_with("target-")
|| name == "node_modules"
|| name == "__pycache__"
|| name == "pydeps"
|| name == "site-packages"
|| name == "venv"
|| name == "dist"
|| name == "build"
|| name == "site"
{
continue;
}
if path.is_dir() {
visit_files(root, &path, visitor);
continue;
}
let ext = path.extension().and_then(OsStr::to_str).unwrap_or("");
let basename = path.file_name().and_then(OsStr::to_str).unwrap_or("");
let scannable = matches!(
ext,
"rs" | "py" | "toml" | "yml" | "yaml" | "sh" | "bash" | "json"
) || basename == "build.rs"
|| basename == "Makefile";
if !scannable {
continue;
}
println!("cargo:rerun-if-changed={}", path.display());
let content = match fs::read_to_string(&path) {
Ok(s) => s,
Err(_) => continue,
};
let rel = path.strip_prefix(root).unwrap_or(&path).to_path_buf();
visitor(&rel, &content);
}
}
fn scan_for_underscore_fn_args(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("fn ") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
let n = lines.len();
let mut idx = 0usize;
while idx < n {
let stripped = stripped_lines
.get(idx)
.map(String::as_str)
.unwrap_or(lines[idx]);
if !line_has_keyword(stripped, "fn") {
idx += 1;
continue;
}
let Some(fn_pos) = locate_fn_keyword(stripped) else {
idx += 1;
continue;
};
let after_fn = &stripped[fn_pos + 2..];
let trimmed_after = after_fn.trim_start();
let first_byte = trimmed_after.as_bytes().first().copied();
let is_definition =
matches!(first_byte, Some(b) if b.is_ascii_alphabetic() || b == b'_');
if !is_definition {
idx += 1;
continue;
}
let (sig_start, sig_end_line, sig_end_col_excl) = match find_fn_body_at(&lines, idx) {
Some((_, (open, _close))) => {
let open_line = &stripped_lines[open];
let col = open_line.find('{').unwrap_or(open_line.len());
(idx, open, col)
}
None => {
let mut paren: i32 = 0;
let mut brack: i32 = 0;
let mut found: Option<(usize, usize)> = None;
let limit = (idx + 64).min(n);
'outer: for (rel, sj) in stripped_lines[idx..limit].iter().enumerate() {
let j = idx + rel;
for (k, b) in sj.as_bytes().iter().enumerate() {
match *b {
b'(' => paren += 1,
b')' => paren -= 1,
b'[' => brack += 1,
b']' => brack -= 1,
b';' if paren == 0 && brack == 0 => {
found = Some((j, k));
break 'outer;
}
b'{' if paren == 0 && brack == 0 => {
break 'outer;
}
_ => {}
}
}
}
match found {
Some((j, k)) => (idx, j, k),
None => {
idx += 1;
continue;
}
}
}
};
let mut sig_text = String::new();
let mut line_offsets: Vec<(usize, usize)> = Vec::new();
for (rel, stripped_line) in stripped_lines[sig_start..=sig_end_line].iter().enumerate()
{
let j = sig_start + rel;
let part = if j == sig_end_line {
&stripped_line[..sig_end_col_excl]
} else {
stripped_line.as_str()
};
line_offsets.push((sig_text.len(), j));
sig_text.push_str(part);
sig_text.push('\n');
}
let sig_bytes = sig_text.as_bytes();
let paren_open = {
let mut ang: i32 = 0;
let mut found: Option<usize> = None;
for (k, &b) in sig_bytes.iter().enumerate() {
match b {
b'<' => ang += 1,
b'>' => {
if ang > 0 {
ang -= 1;
}
}
b'(' if ang == 0 => {
found = Some(k);
break;
}
_ => {}
}
}
match found {
Some(p) => p,
None => {
idx = sig_end_line + 1;
continue;
}
}
};
let Some(close_rel) = find_matching_paren(&sig_bytes[paren_open + 1..]) else {
idx = sig_end_line + 1;
continue;
};
let params_inner = &sig_text[paren_open + 1..paren_open + 1 + close_rel];
let inner_bytes = params_inner.as_bytes();
let mut angle: i32 = 0;
let mut paren_d: i32 = 0;
let mut brack_d: i32 = 0;
let mut start_byte: usize = 0;
let mut params: Vec<(usize, usize)> = Vec::new();
let mut k = 0usize;
while k < inner_bytes.len() {
let b = inner_bytes[k];
match b {
b'(' => paren_d += 1,
b')' => paren_d -= 1,
b'[' => brack_d += 1,
b']' => brack_d -= 1,
b'<' => angle += 1,
b'>' => {
if angle > 0 {
angle -= 1;
}
}
b',' if angle == 0 && paren_d == 0 && brack_d == 0 => {
params.push((start_byte, k));
start_byte = k + 1;
}
_ => {}
}
k += 1;
}
if start_byte <= inner_bytes.len() {
params.push((start_byte, inner_bytes.len()));
}
for (ps, pe) in params {
let raw_param = ¶ms_inner[ps..pe];
let mut p = raw_param.trim_start();
while p.starts_with("#[") {
if let Some(close) = p.find(']') {
p = p[close + 1..].trim_start();
} else {
break;
}
}
loop {
let before = p;
if p.starts_with("&mut ") {
p = p[5..].trim_start();
} else if p.starts_with('&') {
p = p[1..].trim_start();
} else if p.starts_with("mut ") {
p = p[4..].trim_start();
}
if p == before {
break;
}
}
let pb = p.as_bytes();
if pb.is_empty() {
continue;
}
if pb.iter().all(|c| c.is_ascii_whitespace()) {
continue;
}
let mut end = 0usize;
while end < pb.len() && (pb[end] == b'_' || pb[end].is_ascii_alphanumeric()) {
end += 1;
}
if end == 0 {
continue;
}
let name = &p[..end];
let rest = p[end..].trim_start();
if !rest.starts_with(':') {
continue;
}
if name.starts_with('_') {
let type_after_colon = rest[1..].trim_start();
if type_after_colon.starts_with("Python<") {
continue;
}
let name_off_in_param = raw_param.find(name).unwrap_or(0);
let abs = paren_open + 1 + ps + name_off_in_param;
let mut hit_line = sig_start;
for (off, ln) in &line_offsets {
if *off <= abs {
hit_line = *ln;
} else {
break;
}
}
let raw = lines.get(hit_line).copied().unwrap_or("");
offenders.push((rel.to_path_buf(), hit_line + 1, raw.to_string()));
}
}
idx = sig_end_line + 1;
}
});
}
fn locate_fn_keyword(stripped: &str) -> Option<usize> {
let bytes = stripped.as_bytes();
let mut i = 0usize;
while i + 2 <= bytes.len() {
if &bytes[i..i + 2] == b"fn" {
let before_ok = i == 0 || !is_ident_byte(bytes[i - 1]);
let after_ok = i + 2 == bytes.len() || !is_ident_byte(bytes[i + 2]);
if before_ok && after_ok {
return Some(i);
}
}
i += 1;
}
None
}
fn scan_for_useless_tests(root: &Path, dir: &Path, offenders: &mut Vec<(PathBuf, usize, String)>) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("#[test]") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
let n = lines.len();
let mut i = 0usize;
while i < n {
let s = stripped_lines
.get(i)
.map(String::as_str)
.unwrap_or(lines[i]);
if !s.contains("#[test]") {
i += 1;
continue;
}
let mut has_should_panic = s.contains("#[should_panic");
let mut back = i;
while back > 0 {
back -= 1;
let prev = stripped_lines
.get(back)
.map(String::as_str)
.unwrap_or(lines[back]);
let pt = prev.trim();
if pt.is_empty() || pt.starts_with("//") {
continue;
}
if pt.starts_with("#[") || pt.starts_with("#![") {
if prev.contains("#[should_panic") {
has_should_panic = true;
}
continue;
}
break;
}
let mut j = i + 1;
while j < n {
let sj = stripped_lines
.get(j)
.map(String::as_str)
.unwrap_or(lines[j]);
let t = sj.trim();
if t.is_empty() {
j += 1;
continue;
}
if t.starts_with("//") {
j += 1;
continue;
}
if t.starts_with("#[") || t.starts_with("#![") {
if sj.contains("#[should_panic") {
has_should_panic = true;
}
j += 1;
continue;
}
if line_has_keyword(sj, "fn") {
break;
}
j = n;
break;
}
if j >= n {
i += 1;
continue;
}
if has_should_panic {
i = j + 1;
continue;
}
let Some((_sig, (open, close))) = find_fn_body_at(&lines, j) else {
i = j + 1;
continue;
};
let mut found = false;
for (offset, raw_line) in lines[open..=close].iter().enumerate() {
let k = open + offset;
let line_s = stripped_lines
.get(k)
.map(String::as_str)
.unwrap_or(raw_line);
if line_s.contains("assert!(")
|| line_s.contains("assert_eq!(")
|| line_s.contains("assert_ne!(")
|| line_s.contains("debug_assert!(")
|| line_s.contains("debug_assert_eq!(")
|| line_s.contains("debug_assert_ne!(")
|| line_s.contains("panic!(")
|| line_s.contains("unreachable!(")
|| line_s.contains("unimplemented!(")
|| line_s.contains("todo!(")
|| line_contains_propagating_question(line_s)
|| line_contains_assertion_helper_macro(line_s)
{
found = true;
break;
}
}
if !found {
let raw = lines.get(i).copied().unwrap_or("");
if rel
.to_string_lossy()
.contains("pyffi_fitted_family_roundtrip_bug")
{
eprintln!(
"DEBUG useless_test: file={} open={} close={}",
rel.display(),
open,
close
);
for k in open..=close {
let raw_l = lines.get(k).copied().unwrap_or("");
let strp = stripped_lines.get(k).map(String::as_str).unwrap_or(raw_l);
eprintln!(" L{}: RAW=[{}]", k + 1, raw_l);
eprintln!(" STR=[{}]", strp);
}
}
offenders.push((rel.to_path_buf(), i + 1, raw.to_string()));
}
i = close + 1;
}
});
}
fn line_contains_assertion_helper_macro(stripped: &str) -> bool {
let bytes = stripped.as_bytes();
let n = bytes.len();
let mut i = 0usize;
while i < n {
if bytes[i] != b'!' {
i += 1;
continue;
}
let next = bytes.get(i + 1).copied();
if !matches!(next, Some(b'(') | Some(b'[') | Some(b'{')) {
i += 1;
continue;
}
let mut start = i;
while start > 0 {
let b = bytes[start - 1];
if b == b'_' || b.is_ascii_alphanumeric() {
start -= 1;
} else {
break;
}
}
if start == i {
i += 1;
continue;
}
let name = &stripped[start..i];
if name.starts_with("assert_")
|| name.starts_with("expect_")
|| name.starts_with("require_")
|| name.starts_with("ensure_")
{
return true;
}
i += 1;
}
false
}
fn line_contains_propagating_question(stripped: &str) -> bool {
let bytes = stripped.as_bytes();
let n = bytes.len();
let mut i = 0usize;
while i < n {
if bytes[i] == b'?' {
let mut k = i + 1;
while k < n && bytes[k] == b' ' {
k += 1;
}
if k == n {
return true;
}
let c = bytes[k];
if matches!(c, b';' | b',' | b'.' | b')') {
return true;
}
}
i += 1;
}
false
}
const HISTORY_LEDGER_FILENAME: &str = "ban_history.txt";
const HISTORY_MARKERS: &[&str] = &["unimplemented!(", "todo!(", "unreachable!(", "panic!("];
const HISTORY_BODY_REJECT_MACROS: &[&str] = &[
"unimplemented!(",
"todo!(",
"unreachable!(",
"panic!(",
"assert!(false",
"process::exit(",
"process::abort(",
];
const HISTORY_MIN_SUBSTANTIVE_BODY_LINES: usize = 2;
fn run_unimplemented_history_audit(
manifest_dir: &Path,
violations: &mut Vec<(PathBuf, usize, String, String)>,
) {
let ledger_path = manifest_dir.join(HISTORY_LEDGER_FILENAME);
println!("cargo:rerun-if-changed={}", ledger_path.display());
let mut current: std::collections::BTreeMap<(String, String), (PathBuf, usize, Vec<String>)> =
std::collections::BTreeMap::new();
let mut file_contents: std::collections::BTreeMap<String, String> =
std::collections::BTreeMap::new();
visit_files(manifest_dir, manifest_dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
file_contents.insert(rel_str.clone(), content.to_string());
let lines: Vec<&str> = content.lines().collect();
let mask = compute_test_mask(content, rel);
for (idx, line) in lines.iter().enumerate() {
if mask.get(idx).copied().unwrap_or(false) {
continue;
}
let stripped = strip_strings_and_comments(line);
for &marker in HISTORY_MARKERS {
if !stripped.contains(marker) {
continue;
}
if let Some((sig, (open, _close))) = find_enclosing_fn(&lines, idx) {
let kind = marker.trim_end_matches('(').to_string();
let entry = current
.entry((rel_str.clone(), sig.clone()))
.or_insert_with(|| (rel.to_path_buf(), open + 1, Vec::new()));
if !entry.2.contains(&kind) {
entry.2.push(kind);
}
break;
}
}
}
});
let previous = load_history_ledger(&ledger_path);
let mut next_ledger: std::collections::BTreeMap<(String, String), Vec<String>> =
std::collections::BTreeMap::new();
for ((file, sig), (_, _, kinds)) in ¤t {
next_ledger.insert((file.clone(), sig.clone()), kinds.clone());
}
for ((file, sig), prev_kinds) in &previous {
if current.contains_key(&(file.clone(), sig.clone())) {
continue;
}
let state = match file_contents.get(file) {
None => HistoryBodyState::FnAbsent,
Some(c) => body_state_for_signature(c, sig),
};
match state {
HistoryBodyState::FnAbsent | HistoryBodyState::Substantive => {
}
HistoryBodyState::Trivial {
fn_open_line,
snippet,
} => {
violations.push((
PathBuf::from(file),
fn_open_line + 1,
sig.clone(),
format!(
"marker(s) [{}] removed but body still trivial / panic-shaped — first body line: {}",
prev_kinds.join(","),
snippet,
),
));
next_ledger.insert((file.clone(), sig.clone()), prev_kinds.clone());
}
}
}
save_history_ledger(&ledger_path, &next_ledger);
}
fn load_history_ledger(path: &Path) -> std::collections::BTreeMap<(String, String), Vec<String>> {
let mut out: std::collections::BTreeMap<(String, String), Vec<String>> =
std::collections::BTreeMap::new();
let content = match fs::read_to_string(path) {
Ok(c) => c,
Err(_) => return out,
};
for line in content.lines() {
let trimmed = line.trim_end();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let parts: Vec<&str> = trimmed.splitn(3, '\t').collect();
if parts.len() != 3 {
continue;
}
let file = parts[0].to_string();
let sig = parts[1].to_string();
let kinds: Vec<String> = parts[2]
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect();
out.insert((file, sig), kinds);
}
out
}
fn save_history_ledger(
path: &Path,
ledger: &std::collections::BTreeMap<(String, String), Vec<String>>,
) {
let mut out = String::new();
out.push_str(
"# Persistent audit of `unimplemented!(` / `todo!(` / `unreachable!(`\n\
# sites. Auto-managed by build.rs — do NOT hand-edit. Each non-comment\n\
# line is tab-separated:\n\
# <relative_path>\\t<normalized_fn_signature>\\t<comma_marker_kinds>\n\
# When an entry disappears from the source tree, build.rs inspects the\n\
# enclosing function's body. Trivial bodies (under a few code lines)\n\
# and bodies that still contain any panic-shape macro fail the build,\n\
# so deleting a `unimplemented!` without writing a real implementation\n\
# is caught. Legitimately-implemented removals auto-prune.\n\
#\n\
# Concurrent branches that both remove markers will produce additive\n\
# line-level diff conflicts that resolve by union — the next build\n\
# then re-validates each surviving entry.\n",
);
for ((file, sig), kinds) in ledger {
out.push_str(file);
out.push('\t');
out.push_str(sig);
out.push('\t');
out.push_str(&kinds.join(","));
out.push('\n');
}
match fs::read_to_string(path) {
Ok(existing) if existing == out => return,
_ => {}
}
if fs::write(path, out).is_err() {
}
}
enum HistoryBodyState {
FnAbsent,
Trivial {
fn_open_line: usize,
snippet: String,
},
Substantive,
}
fn body_state_for_signature(content: &str, target_sig: &str) -> HistoryBodyState {
let lines: Vec<&str> = content.lines().collect();
let mut idx = 0;
while idx < lines.len() {
let stripped = strip_strings_and_comments(lines[idx]);
if !line_has_keyword(&stripped, "fn") {
idx += 1;
continue;
}
if let Some((sig, (open, close))) = find_fn_body_at(&lines, idx) {
if sig == target_sig {
let mut code_lines = 0;
let mut first_snippet: Option<String> = None;
for raw in lines.iter().take(close + 1).skip(open) {
let s = strip_strings_and_comments(raw);
let t = s.trim();
if t.is_empty() {
continue;
}
if t.chars().all(|c| matches!(c, '{' | '}' | ' ')) {
continue;
}
for &m in HISTORY_BODY_REJECT_MACROS {
if s.contains(m) {
return HistoryBodyState::Trivial {
fn_open_line: open,
snippet: raw.trim().chars().take(120).collect::<String>(),
};
}
}
if first_snippet.is_none() {
first_snippet = Some(raw.trim().chars().take(120).collect::<String>());
}
code_lines += 1;
}
if code_lines < HISTORY_MIN_SUBSTANTIVE_BODY_LINES {
return HistoryBodyState::Trivial {
fn_open_line: open,
snippet: first_snippet.unwrap_or_else(|| "<empty body>".to_string()),
};
}
return HistoryBodyState::Substantive;
}
idx = close + 1;
continue;
}
idx += 1;
}
HistoryBodyState::FnAbsent
}
fn find_enclosing_fn(lines: &[&str], at_line: usize) -> Option<(String, (usize, usize))> {
let mut start = at_line + 1;
while start > 0 {
start -= 1;
let stripped = strip_strings_and_comments(lines[start]);
if !line_has_keyword(&stripped, "fn") {
continue;
}
if let Some((sig, (open, close))) = find_fn_body_at(lines, start)
&& open <= at_line
&& at_line <= close
{
return Some((sig, (open, close)));
}
}
None
}
fn find_fn_body_at(lines: &[&str], fn_line: usize) -> Option<(String, (usize, usize))> {
let mut stripped: Vec<String> = Vec::with_capacity(lines.len() - fn_line);
{
let mut in_str = false;
let mut quote: u8 = 0;
let mut hashes: u8 = 0;
for line in &lines[fn_line..] {
let (s, ns, nq, nh) =
strip_strings_and_comments_stateful_raw(line, in_str, quote, hashes);
stripped.push(s);
in_str = ns;
quote = nq;
hashes = nh;
}
}
let mut depth: i32 = 0;
let mut body_open: Option<usize> = None;
for (rel, s) in stripped.iter().enumerate() {
let j = fn_line + rel;
for b in s.bytes() {
match b {
b'{' => {
depth += 1;
if body_open.is_none() {
body_open = Some(j);
}
}
b'}' => {
depth -= 1;
if depth == 0
&& let Some(open) = body_open
{
let mut sig = String::new();
for k in fn_line..=open {
let ss = &stripped[k - fn_line];
let cut = if k == open {
ss.find('{').unwrap_or(ss.len())
} else {
ss.len()
};
sig.push_str(&ss[..cut]);
sig.push(' ');
}
let normalized: String =
sig.split_whitespace().collect::<Vec<_>>().join(" ");
return Some((normalized, (open, j)));
}
}
_ => {}
}
}
}
None
}
fn dodge_name_substrings() -> &'static [&'static str] {
&[
"discard_",
"_discard_",
"swallow_",
"_swallow_",
"_for_fixed_lambda",
"_for_fixed_arg",
"_for_unused",
"_for_no_op",
"_no_op_",
"silence_",
"ignore_unused",
"eat_unused",
"consume_unused",
"intentionally_unused",
"placeholder_for_",
"dummy_for_",
]
}
fn scan_for_dodge_named_fns(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String, String)>,
) {
let needles = dodge_name_substrings();
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("fn ") {
return;
}
let stripped_lines = strip_file_lines(content);
for (idx, line) in content.lines().enumerate() {
let stripped = stripped_lines.get(idx).map(String::as_str).unwrap_or(line);
if !line_has_keyword(stripped, "fn") {
continue;
}
let Some(fn_pos) = locate_fn_keyword(stripped) else {
continue;
};
let bytes = stripped.as_bytes();
let mut j = fn_pos + 2;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() {
continue;
}
if !(bytes[j].is_ascii_alphabetic() || bytes[j] == b'_') {
continue;
}
let name_start = j;
while j < bytes.len() && is_ident_byte(bytes[j]) {
j += 1;
}
if j == name_start {
continue;
}
let name = &stripped[name_start..j];
let lower = name.to_ascii_lowercase();
for needle in needles {
if lower.contains(needle) {
offenders.push((
rel.to_path_buf(),
idx + 1,
name.to_string(),
line.to_string(),
));
break;
}
}
}
});
}
fn scan_for_noop_self_consuming_fns(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("fn ") {
return;
}
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
let n = lines.len();
let mut i = 0usize;
while i < n {
let s = stripped_lines
.get(i)
.map(String::as_str)
.unwrap_or(lines[i]);
if !line_has_keyword(s, "fn") {
i += 1;
continue;
}
let Some((sig, (open, close))) = find_fn_body_at(&lines, i) else {
i += 1;
continue;
};
if !first_param_is_by_value_self(&sig) {
i = open + 1;
continue;
}
if fn_body_is_empty(&stripped_lines, open, close) {
offenders.push((rel.to_path_buf(), i + 1, lines[i].to_string()));
}
i = close + 1;
}
});
}
fn first_param_is_by_value_self(sig: &str) -> bool {
let bytes = sig.as_bytes();
let mut i = 0usize;
let mut fn_pos: Option<usize> = None;
while i + 2 <= bytes.len() {
if &bytes[i..i + 2] == b"fn"
&& (i == 0 || !is_ident_byte(bytes[i - 1]))
&& (i + 2 == bytes.len() || !is_ident_byte(bytes[i + 2]))
{
fn_pos = Some(i);
break;
}
i += 1;
}
let Some(fp) = fn_pos else {
return false;
};
let mut j = fp + 2;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || !(bytes[j].is_ascii_alphabetic() || bytes[j] == b'_') {
return false;
}
while j < bytes.len() && is_ident_byte(bytes[j]) {
j += 1;
}
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j < bytes.len() && bytes[j] == b'<' {
let mut depth: i32 = 0;
while j < bytes.len() {
match bytes[j] {
b'<' => depth += 1,
b'>' => {
depth -= 1;
if depth == 0 {
j += 1;
break;
}
}
_ => {}
}
j += 1;
}
}
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || bytes[j] != b'(' {
return false;
}
let params_start = j + 1;
let Some(end) = find_matching_paren(&bytes[params_start..]) else {
return false;
};
let params = &sig[params_start..params_start + end];
let first = first_top_level_segment(params);
let first_trim = first.trim();
if first_trim.is_empty() {
return false;
}
if first_trim.starts_with('&') {
return false;
}
let after_mut = first_trim
.strip_prefix("mut ")
.map(str::trim_start)
.unwrap_or(first_trim);
if after_mut == "self" {
return true;
}
if let Some(rest) = after_mut.strip_prefix("self") {
if let Some(&b) = rest.as_bytes().first()
&& is_ident_byte(b)
{
return false;
}
let r = rest.trim_start();
if let Some(tail) = r.strip_prefix(':') {
let ty = tail.trim_start();
if ty.starts_with('&') {
return false;
}
return !ty.is_empty();
}
}
false
}
fn first_top_level_segment(params: &str) -> &str {
let bytes = params.as_bytes();
let mut angle: i32 = 0;
let mut paren: i32 = 0;
let mut bracket: i32 = 0;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'<' => angle += 1,
b'>' => {
if angle > 0 {
angle -= 1;
}
}
b'(' => paren += 1,
b')' => {
if paren > 0 {
paren -= 1;
}
}
b'[' => bracket += 1,
b']' => {
if bracket > 0 {
bracket -= 1;
}
}
b',' if angle == 0 && paren == 0 && bracket == 0 => {
return ¶ms[..i];
}
_ => {}
}
i += 1;
}
params
}
fn fn_body_is_empty(stripped_lines: &[String], open: usize, close: usize) -> bool {
if open > close || open >= stripped_lines.len() || close >= stripped_lines.len() {
return false;
}
let open_line = stripped_lines[open].as_str();
let Some(open_brace) = open_line.find('{') else {
return false;
};
if open == close {
let tail = &open_line[open_brace + 1..];
let Some(close_brace) = tail.rfind('}') else {
return false;
};
return tail[..close_brace].chars().all(char::is_whitespace);
}
let open_tail = &open_line[open_brace + 1..];
if !open_tail.chars().all(char::is_whitespace) {
return false;
}
for line in &stripped_lines[open + 1..close] {
if !line.chars().all(char::is_whitespace) {
return false;
}
}
let close_line = stripped_lines[close].as_str();
let Some(close_brace) = close_line.rfind('}') else {
return false;
};
close_line[..close_brace].chars().all(char::is_whitespace)
}
fn scan_for_stub_function_bodies(
root: &Path,
dir: &Path,
offenders: &mut Vec<(PathBuf, usize, String)>,
) {
visit_files(root, dir, &mut |rel, content| {
let rel_str = rel.to_string_lossy().replace('\\', "/");
if rel_str == "build.rs" {
return;
}
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
if !content.contains("fn ") {
return;
}
let mask = compute_test_mask(content, rel);
let lines: Vec<&str> = content.lines().collect();
let stripped_lines = strip_file_lines(content);
let n = lines.len();
let mut i = 0usize;
while i < n {
let s = stripped_lines
.get(i)
.map(String::as_str)
.unwrap_or(lines[i]);
if !line_has_keyword(s, "fn") {
i += 1;
continue;
}
if mask.get(i).copied().unwrap_or(false) {
i += 1;
continue;
}
if fn_signature_ends_with_semicolon(&stripped_lines, i) {
i += 1;
continue;
}
let Some((sig, (open, close))) = find_fn_body_at(&lines, i) else {
i += 1;
continue;
};
let Some(param_count) = signature_param_count(&sig) else {
i = close + 1;
continue;
};
if param_count < 2 {
i = close + 1;
continue;
}
let body = extract_body_text(&stripped_lines, open, close);
if body_is_trivial_sentinel(&body) {
offenders.push((rel.to_path_buf(), i + 1, lines[i].to_string()));
}
i = close + 1;
}
});
}
fn fn_signature_ends_with_semicolon(stripped_lines: &[String], fn_line: usize) -> bool {
let mut paren: i32 = 0;
let mut brack: i32 = 0;
let mut angle: i32 = 0;
let limit = (fn_line + 64).min(stripped_lines.len());
for line in &stripped_lines[fn_line..limit] {
for &b in line.as_bytes() {
match b {
b'(' => paren += 1,
b')' => paren -= 1,
b'[' => brack += 1,
b']' => brack -= 1,
b'<' => angle += 1,
b'>' => {
if angle > 0 {
angle -= 1;
}
}
b';' if paren == 0 && brack == 0 => return true,
b'{' if paren == 0 && brack == 0 => return false,
_ => {}
}
}
}
false
}
fn signature_param_count(sig: &str) -> Option<usize> {
let bytes = sig.as_bytes();
let mut i = 0usize;
let mut fn_pos: Option<usize> = None;
while i + 2 <= bytes.len() {
if &bytes[i..i + 2] == b"fn"
&& (i == 0 || !is_ident_byte(bytes[i - 1]))
&& (i + 2 == bytes.len() || !is_ident_byte(bytes[i + 2]))
{
fn_pos = Some(i);
break;
}
i += 1;
}
let fp = fn_pos?;
let mut j = fp + 2;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || !(bytes[j].is_ascii_alphabetic() || bytes[j] == b'_') {
return None;
}
while j < bytes.len() && is_ident_byte(bytes[j]) {
j += 1;
}
let mut k = j;
while k < bytes.len() && bytes[k].is_ascii_whitespace() {
k += 1;
}
if k < bytes.len() && bytes[k] == b'<' {
let mut ang: i32 = 0;
while k < bytes.len() {
match bytes[k] {
b'<' => ang += 1,
b'>' => {
ang -= 1;
if ang == 0 {
k += 1;
break;
}
}
_ => {}
}
k += 1;
}
}
let mut ang2: i32 = 0;
let mut open: Option<usize> = None;
while k < bytes.len() {
match bytes[k] {
b'<' => ang2 += 1,
b'>' => {
if ang2 > 0 {
ang2 -= 1;
}
}
b'(' if ang2 == 0 => {
open = Some(k);
break;
}
_ => {}
}
k += 1;
}
let op = open?;
let inner_start = op + 1;
let end_rel = find_matching_paren(&bytes[inner_start..])?;
let inner = &sig[inner_start..inner_start + end_rel];
if inner.trim().is_empty() {
return Some(0);
}
let mut count = 0usize;
let mut a: i32 = 0;
let mut p: i32 = 0;
let mut br: i32 = 0;
let mut seen_nonws = false;
for &c in inner.as_bytes() {
match c {
b'<' => {
a += 1;
seen_nonws = true;
}
b'>' => {
if a > 0 {
a -= 1;
}
seen_nonws = true;
}
b'(' => {
p += 1;
seen_nonws = true;
}
b')' => {
p -= 1;
seen_nonws = true;
}
b'[' => {
br += 1;
seen_nonws = true;
}
b']' => {
br -= 1;
seen_nonws = true;
}
b',' if a == 0 && p == 0 && br == 0 => {
if seen_nonws {
count += 1;
}
seen_nonws = false;
}
x if !(x as char).is_whitespace() => seen_nonws = true,
_ => {}
}
}
if seen_nonws {
count += 1;
}
Some(count)
}
fn extract_body_text(stripped_lines: &[String], open: usize, close: usize) -> String {
if open > close || close >= stripped_lines.len() {
return String::new();
}
let mut parts: Vec<String> = Vec::new();
for k in open..=close {
let line = stripped_lines[k].as_str();
let segment: &str = if k == open && k == close {
let after_open = line.find('{').map(|p| p + 1).unwrap_or(line.len());
let before_close = line[after_open..]
.rfind('}')
.map(|p| after_open + p)
.unwrap_or(line.len());
&line[after_open..before_close]
} else if k == open {
let after_open = line.find('{').map(|p| p + 1).unwrap_or(line.len());
&line[after_open..]
} else if k == close {
let before_close = line.rfind('}').unwrap_or(line.len());
&line[..before_close]
} else {
line
};
let t = segment.trim();
if !t.is_empty() {
parts.push(t.to_string());
}
}
parts.join(" ").trim().to_string()
}
fn body_is_trivial_sentinel(body: &str) -> bool {
let after_prologue = strip_validation_prologue(body);
let mut s = after_prologue.trim();
if let Some(stripped) = s.strip_suffix(';') {
s = stripped.trim_end();
}
if s.is_empty() {
return false;
}
if matches!(
s,
"None"
| "Ok(())"
| "Default::default()"
| "Vec::new()"
| "vec![]"
| "()"
| "HashMap::new()"
| "BTreeMap::new()"
| "HashSet::new()"
| "BTreeSet::new()"
| "VecDeque::new()"
| "String::new()"
| "Some(Default::default())"
| "true"
| "false"
| "\"\""
) {
return true;
}
if let Some(prefix) = s.strip_suffix("::default()")
&& is_path_like(prefix)
{
return true;
}
if let Some(inner) = s.strip_prefix("Some(").and_then(|r| r.strip_suffix(')'))
&& let Some(prefix) = inner.strip_suffix("::default()")
&& is_path_like(prefix)
{
return true;
}
if let Some(inner) = s.strip_prefix("Err(").and_then(|r| r.strip_suffix(')'))
&& err_arg_is_string_literal(inner.trim())
{
return true;
}
if matches!(
s,
"Array1::zeros(0)" | "Array2::zeros((0, 0))" | "Array3::zeros((0, 0, 0))"
) {
return true;
}
if is_bare_numeric_literal(s) {
return true;
}
false
}
fn strip_validation_prologue(body: &str) -> &str {
let mut s = body.trim_start();
loop {
if let Some(rest) = strip_leading_assert_call(s) {
s = rest.trim_start();
continue;
}
if let Some(rest) = strip_leading_if_return_guard(s) {
s = rest.trim_start();
continue;
}
if let Some(rest) = strip_leading_drop_call(s) {
s = rest.trim_start();
continue;
}
if let Some(rest) = strip_leading_wildcard_assignment(s) {
s = rest.trim_start();
continue;
}
break;
}
s
}
fn strip_leading_assert_call(s: &str) -> Option<&str> {
for name in ["assert", "assert_eq", "assert_ne", "assert_matches"] {
let Some(after_name) = s.strip_prefix(name) else {
continue;
};
let after_bang = after_name.strip_prefix('!')?;
let after_open_ws = after_bang.trim_start();
let (open, close) = match after_open_ws.as_bytes().first()? {
b'(' => (b'(', b')'),
b'[' => (b'[', b']'),
b'{' => (b'{', b'}'),
_ => return None,
};
let after_open = &after_open_ws[1..];
let bytes = after_open.as_bytes();
let mut depth: i32 = 1;
let mut i = 0usize;
while i < bytes.len() {
let b = bytes[i];
if b == open {
depth += 1;
} else if b == close {
depth -= 1;
if depth == 0 {
let after = after_open[i + 1..].trim_start();
return after.strip_prefix(';');
}
}
i += 1;
}
return None;
}
None
}
fn strip_leading_if_return_guard(s: &str) -> Option<&str> {
let after_if = s.strip_prefix("if ")?;
let bytes = after_if.as_bytes();
let mut paren: i32 = 0;
let mut brack: i32 = 0;
let mut i = 0usize;
let open_brace = loop {
if i >= bytes.len() {
return None;
}
match bytes[i] {
b'(' => paren += 1,
b')' => paren -= 1,
b'[' => brack += 1,
b']' => brack -= 1,
b'{' if paren == 0 && brack == 0 => break i,
_ => {}
}
i += 1;
};
let rest_after_brace = &after_if[open_brace + 1..];
let bytes = rest_after_brace.as_bytes();
let mut depth: i32 = 1;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'{' => depth += 1,
b'}' => {
depth -= 1;
if depth == 0 {
let inside = rest_after_brace[..i].trim();
if inside.starts_with("return ")
&& inside.ends_with(';')
&& count_top_level_semicolons(inside) == 1
{
let ret_expr = inside["return ".len()..inside.len() - 1].trim();
if return_expr_is_trivial_sentinel(ret_expr) {
return Some(&rest_after_brace[i + 1..]);
}
}
return None;
}
}
_ => {}
}
i += 1;
}
None
}
fn count_top_level_semicolons(s: &str) -> usize {
let bytes = s.as_bytes();
let mut paren: i32 = 0;
let mut brack: i32 = 0;
let mut brace: i32 = 0;
let mut angle: i32 = 0;
let mut count = 0usize;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'(' => paren += 1,
b')' => paren -= 1,
b'[' => brack += 1,
b']' => brack -= 1,
b'{' => brace += 1,
b'}' => brace -= 1,
b'<' => angle += 1,
b'>' => {
if angle > 0 {
angle -= 1;
}
}
b';' if paren == 0 && brack == 0 && brace == 0 && angle == 0 => {
count += 1;
}
_ => {}
}
i += 1;
}
count
}
fn return_expr_is_trivial_sentinel(expr: &str) -> bool {
let s = expr.trim();
if matches!(
s,
"None"
| "Ok(())"
| "Default::default()"
| "Vec::new()"
| "vec![]"
| "()"
| "HashMap::new()"
| "BTreeMap::new()"
| "HashSet::new()"
| "BTreeSet::new()"
| "VecDeque::new()"
| "String::new()"
| "Some(Default::default())"
| "true"
| "false"
| "\"\""
| "Array1::zeros(0)"
| "Array2::zeros((0, 0))"
| "Array3::zeros((0, 0, 0))"
) {
return true;
}
if let Some(prefix) = s.strip_suffix("::default()")
&& is_path_like(prefix)
{
return true;
}
if let Some(inner) = s.strip_prefix("Some(").and_then(|r| r.strip_suffix(')'))
&& let Some(prefix) = inner.strip_suffix("::default()")
&& is_path_like(prefix)
{
return true;
}
if let Some(inner) = s.strip_prefix("Ok(").and_then(|r| r.strip_suffix(')'))
&& matches!(
inner.trim(),
"" | "Array1::zeros(0)" | "Array2::zeros((0, 0))" | "Array3::zeros((0, 0, 0))"
)
{
return true;
}
is_bare_numeric_literal(s)
}
fn strip_leading_drop_call(s: &str) -> Option<&str> {
let after_open = s.strip_prefix("drop(")?;
let bytes = after_open.as_bytes();
let mut depth: i32 = 1;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'(' => depth += 1,
b')' => {
depth -= 1;
if depth == 0 {
let after = after_open[i + 1..].trim_start();
return after.strip_prefix(';');
}
}
_ => {}
}
i += 1;
}
None
}
fn strip_leading_wildcard_assignment(s: &str) -> Option<&str> {
let after_underscore = s.strip_prefix('_')?;
if after_underscore
.as_bytes()
.first()
.is_some_and(|b| is_ident_byte(*b))
{
return None;
}
let rest = after_underscore.trim_start().strip_prefix('=')?;
if rest.as_bytes().first() == Some(&b'=') {
return None;
}
let rest = rest.trim_start();
let bytes = rest.as_bytes();
let mut paren: i32 = 0;
let mut brack: i32 = 0;
let mut brace: i32 = 0;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'(' => paren += 1,
b')' => paren -= 1,
b'[' => brack += 1,
b']' => brack -= 1,
b'{' => brace += 1,
b'}' => brace -= 1,
b';' if paren == 0 && brack == 0 && brace == 0 => {
return Some(&rest[i + 1..]);
}
_ => {}
}
i += 1;
}
None
}
fn is_path_like(s: &str) -> bool {
let s = s.trim();
if s.is_empty() {
return false;
}
s.bytes()
.all(|b| is_ident_byte(b) || b == b':' || b == b'<' || b == b'>' || b == b',' || b == b' ')
}
fn err_arg_is_string_literal(arg: &str) -> bool {
let bytes = arg.as_bytes();
if bytes.is_empty() || bytes[0] != b'"' {
return false;
}
let mut i = 1usize;
while i < bytes.len() {
if bytes[i] == b'\\' && i + 1 < bytes.len() {
i += 2;
continue;
}
if bytes[i] == b'"' {
i += 1;
let tail = arg[i..].trim();
return tail.is_empty()
|| tail == ".into()"
|| tail == ".to_string()"
|| tail == ".to_owned()";
}
i += 1;
}
false
}
fn is_bare_numeric_literal(s: &str) -> bool {
let bytes = s.as_bytes();
if bytes.is_empty() {
return false;
}
let mut i = 0usize;
let mut saw_digit = false;
while i < bytes.len() && (bytes[i].is_ascii_digit() || bytes[i] == b'_') {
if bytes[i].is_ascii_digit() {
saw_digit = true;
}
i += 1;
}
if !saw_digit {
return false;
}
if i < bytes.len() && bytes[i] == b'.' {
i += 1;
while i < bytes.len() && (bytes[i].is_ascii_digit() || bytes[i] == b'_') {
i += 1;
}
}
while i < bytes.len() && is_ident_byte(bytes[i]) {
i += 1;
}
i == bytes.len()
}
fn scan_for_src_items_used_only_by_tests(
root: &Path,
offenders: &mut Vec<(PathBuf, usize, String, String)>,
unreferenced_pub_scoped: &mut Vec<(PathBuf, usize, String, String)>,
) {
const EXEMPT_NAMES: &[&str] = &[
"new",
"default",
"iter",
"len",
"is_empty",
"clone",
"from",
"into",
"as_ref",
"as_mut",
"next",
"build",
"with",
"to_string",
"display",
"fmt",
"index",
"borrow",
"drop",
"main",
"deref",
"deref_mut",
"hash",
"eq",
"ne",
"cmp",
"partial_cmp",
"iter_mut",
"into_iter",
"as_slice",
"as_str",
];
struct SrcFile {
rel: PathBuf,
content: String,
mask: Vec<bool>,
stripped: Vec<String>,
trait_impl: Vec<bool>,
}
let mut src_files: Vec<SrcFile> = Vec::new();
let mut test_contents: Vec<(PathBuf, String)> = Vec::new();
visit_files(root, root, &mut |rel, content| {
if rel.extension().and_then(OsStr::to_str) != Some("rs") {
return;
}
let rel_str = rel.to_string_lossy().replace('\\', "/");
let is_test = rel_str.starts_with("tests/")
|| rel_str.starts_with("bench/")
|| rel_str.starts_with("benches/")
|| path_matches_crates_test(&rel_str);
if is_test {
test_contents.push((rel.to_path_buf(), content.to_string()));
return;
}
let is_src = rel_str.starts_with("src/")
|| (rel_str.starts_with("crates/") && rel_str.contains("/src/"));
if !is_src {
return;
}
let mask = compute_test_mask(content, rel);
let stripped = strip_file_lines(content);
let trait_impl = compute_trait_impl_mask(content);
src_files.push(SrcFile {
rel: rel.to_path_buf(),
content: content.to_string(),
mask,
stripped,
trait_impl,
});
});
let mut defs: Vec<(usize, usize, String, Visibility)> = Vec::new();
for (fi, sf) in src_files.iter().enumerate() {
for (idx, stripped) in sf.stripped.iter().enumerate() {
if sf.mask.get(idx).copied().unwrap_or(false) {
continue;
}
let trimmed = stripped.trim_start();
let (vis, rest) = strip_leading_visibility(trimmed);
if matches!(vis, Visibility::Public) {
continue;
}
let rest = strip_leading_item_modifiers(rest);
let Some(ident) = extract_item_ident(rest) else {
continue;
};
if ident.starts_with('_') || ident.len() <= 2 {
continue;
}
if EXEMPT_NAMES.contains(&ident.as_str()) {
continue;
}
if sf.trait_impl.get(idx).copied().unwrap_or(false) {
continue;
}
defs.push((fi, idx, ident, vis));
}
}
if defs.is_empty() {
return;
}
let mut per_file_tokens: Vec<std::collections::HashSet<String>> =
Vec::with_capacity(src_files.len());
for sf in &src_files {
let mut set: std::collections::HashSet<String> = std::collections::HashSet::new();
for (idx, stripped) in sf.stripped.iter().enumerate() {
if sf.mask.get(idx).copied().unwrap_or(false) {
continue;
}
extract_ident_tokens_into(stripped, &mut set);
}
per_file_tokens.push(set);
}
let mut defining_file_token_lines: Vec<Vec<std::collections::HashSet<String>>> =
Vec::with_capacity(src_files.len());
for sf in &src_files {
let mut v: Vec<std::collections::HashSet<String>> = Vec::with_capacity(sf.stripped.len());
for (idx, stripped) in sf.stripped.iter().enumerate() {
let mut set: std::collections::HashSet<String> = std::collections::HashSet::new();
if !sf.mask.get(idx).copied().unwrap_or(false) {
extract_ident_tokens_into(stripped, &mut set);
}
v.push(set);
}
defining_file_token_lines.push(v);
}
let mut test_first_hit: std::collections::HashMap<String, PathBuf> =
std::collections::HashMap::new();
for (rel, content) in &test_contents {
let mut local: std::collections::HashSet<String> = std::collections::HashSet::new();
for line in content.lines() {
let stripped = strip_strings_and_comments(line);
extract_ident_tokens_into(&stripped, &mut local);
}
for tok in local {
test_first_hit.entry(tok).or_insert_with(|| rel.clone());
}
}
let mut pub_use_idents: std::collections::HashSet<String> = std::collections::HashSet::new();
for sf in &src_files {
let rel_str = sf.rel.to_string_lossy().replace('\\', "/");
let is_lib_root = rel_str == "src/lib.rs"
|| rel_str.ends_with("/src/lib.rs")
|| rel_str == "src/main.rs"
|| rel_str.ends_with("/src/main.rs");
if !is_lib_root {
continue;
}
for stripped in &sf.stripped {
collect_pub_use_idents(stripped, &mut pub_use_idents);
}
}
for (fi, line_idx, ident, vis) in defs {
if pub_use_idents.contains(&ident) {
continue;
}
let test_hint = test_first_hit.get(&ident).cloned();
let mut prod_consumer = false;
for (other_fi, set) in per_file_tokens.iter().enumerate() {
if other_fi == fi {
continue;
}
if set.contains(&ident) {
prod_consumer = true;
break;
}
}
if !prod_consumer {
let token_lines = &defining_file_token_lines[fi];
for (li, toks) in token_lines.iter().enumerate() {
if li == line_idx {
continue;
}
if toks.contains(&ident) {
prod_consumer = true;
break;
}
}
}
if prod_consumer {
continue;
}
let sf = &src_files[fi];
let raw = sf.content.lines().nth(line_idx).unwrap_or("").to_string();
match (test_hint, vis) {
(Some(hint_path), _) => {
let hint = format!(
"{} (test ref: {})",
ident,
hint_path.to_string_lossy().replace('\\', "/")
);
offenders.push((sf.rel.clone(), line_idx + 1, hint, raw));
}
(None, Visibility::PubScoped) => {
unreferenced_pub_scoped.push((sf.rel.clone(), line_idx + 1, ident.clone(), raw));
}
(None, _) => {
}
}
}
}
#[derive(Clone, Copy)]
enum Visibility {
Private,
Public,
PubScoped,
}
fn strip_leading_visibility(trimmed: &str) -> (Visibility, &str) {
if let Some(rest) = trimmed.strip_prefix("pub(") {
let bytes = rest.as_bytes();
let mut depth = 1i32;
let mut i = 0usize;
while i < bytes.len() {
match bytes[i] {
b'(' => depth += 1,
b')' => {
depth -= 1;
if depth == 0 {
let after = rest[i + 1..].trim_start();
return (Visibility::PubScoped, after);
}
}
_ => {}
}
i += 1;
}
return (Visibility::PubScoped, "");
}
if let Some(rest) = trimmed.strip_prefix("pub ") {
return (Visibility::Public, rest.trim_start());
}
if trimmed == "pub" {
return (Visibility::Public, "");
}
(Visibility::Private, trimmed)
}
fn strip_leading_item_modifiers(mut s: &str) -> &str {
loop {
let before = s;
if let Some(rest) = s.strip_prefix("unsafe ") {
s = rest.trim_start();
continue;
}
if let Some(rest) = s.strip_prefix("async ") {
s = rest.trim_start();
continue;
}
if let Some(rest) = s.strip_prefix("default ") {
s = rest.trim_start();
continue;
}
if s.strip_prefix("const fn ").is_some() {
return &s[6..];
}
if let Some(rest) = s.strip_prefix("extern ") {
let r = rest.trim_start();
if r.starts_with('"') {
if let Some(end) = r[1..].find('"') {
s = r[end + 2..].trim_start();
continue;
}
}
s = r;
continue;
}
if s == before {
return s;
}
}
}
fn extract_item_ident(s: &str) -> Option<String> {
for kw in [
"fn ", "struct ", "enum ", "const ", "static ", "type ", "trait ",
] {
if let Some(rest) = s.strip_prefix(kw) {
let rest = rest.trim_start();
let bytes = rest.as_bytes();
if bytes.is_empty() {
return None;
}
if !(bytes[0].is_ascii_alphabetic() || bytes[0] == b'_') {
return None;
}
let mut end = 0usize;
while end < bytes.len() && is_ident_byte(bytes[end]) {
end += 1;
}
if end == 0 {
return None;
}
return Some(rest[..end].to_string());
}
}
None
}
fn extract_ident_tokens_into(stripped: &str, out: &mut std::collections::HashSet<String>) {
let bytes = stripped.as_bytes();
let n = bytes.len();
let mut i = 0usize;
while i < n {
let b = bytes[i];
if b.is_ascii_alphabetic() || b == b'_' {
let s = i;
while i < n && is_ident_byte(bytes[i]) {
i += 1;
}
out.insert(stripped[s..i].to_string());
} else if b.is_ascii_digit() {
while i < n && (is_ident_byte(bytes[i]) || bytes[i] == b'.') {
i += 1;
}
} else {
i += 1;
}
}
}
fn collect_pub_use_idents(stripped: &str, out: &mut std::collections::HashSet<String>) {
let t = stripped.trim_start();
let rest = if let Some(r) = t.strip_prefix("pub use ") {
r
} else if let Some(r) = t.strip_prefix("pub(crate) use ") {
r
} else {
return;
};
let rest = rest.trim_end().trim_end_matches(';').trim();
collect_use_tree_idents(rest, out);
}
fn collect_use_tree_idents(tree: &str, out: &mut std::collections::HashSet<String>) {
if let Some(brace_start) = tree.find('{') {
let prefix = &tree[..brace_start];
let inside = &tree[brace_start + 1..];
let close = match inside.rfind('}') {
Some(p) => p,
None => return,
};
let inside = &inside[..close];
let mut depth = 0i32;
let mut start = 0usize;
let bytes = inside.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
match b {
b'{' => depth += 1,
b'}' => depth -= 1,
b',' if depth == 0 => {
let seg = inside[start..i].trim();
if !seg.is_empty() {
let combined = format!("{}{}", prefix, seg);
collect_use_tree_idents(&combined, out);
}
start = i + 1;
}
_ => {}
}
}
let tail = inside[start..].trim();
if !tail.is_empty() {
let combined = format!("{}{}", prefix, tail);
collect_use_tree_idents(&combined, out);
}
return;
}
let path = tree.trim();
if path.is_empty() || path == "*" || path.ends_with("::*") {
return;
}
if let Some(as_pos) = path.rfind(" as ") {
let alias = path[as_pos + 4..].trim().trim_end_matches(',').trim();
if !alias.is_empty() && alias != "_" {
out.insert(alias.to_string());
}
return;
}
let last = path.rsplit("::").next().unwrap_or(path).trim();
if !last.is_empty() && last != "*" && last != "self" {
out.insert(last.to_string());
}
}