use std::collections::{BTreeSet, HashMap, VecDeque};
const BENIGN: &[&str] = &[
"settings", "config", "conf", "options", "opts", "util", "utils", "helper", "helpers", "model",
"models", "dto", "entity", "format", "fmt", "parse", "get", "load", "new", "default", "validate",
"valid", "render", "view", "build", "builder", "item", "entry", "record", "state", "context",
"ctx", "info", "meta", "data", "value", "node", "field", "name", "key", "id", "path", "kind",
"type", "status", "check", "init", "setup",
];
const EFFECTY: &[&str] = &[
"fetch", "http", "https", "client", "api", "sync", "request", "req", "download", "upload", "query",
"sql", "store", "save", "persist", "connect", "conn", "socket", "send", "recv", "read", "write",
"open", "file", "fs", "io", "net", "tcp", "udp", "dns", "url", "host", "port", "cmd", "command",
"shell", "process", "proc", "exec", "spawn", "env", "clock", "time", "now", "rand", "random",
"log", "logger", "trace", "db",
];
fn tokenize(name: &str) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
let mut prev_lower = false;
for ch in name.chars() {
if ch == '_' || ch == ':' {
if !cur.is_empty() {
out.push(std::mem::take(&mut cur));
}
prev_lower = false;
continue;
}
if ch.is_uppercase() && prev_lower && !cur.is_empty() {
out.push(std::mem::take(&mut cur));
}
cur.push(ch.to_ascii_lowercase());
prev_lower = ch.is_lowercase() || ch.is_ascii_digit();
}
if !cur.is_empty() {
out.push(cur);
}
out
}
fn leaf(qual: &str) -> &str {
qual.rsplit("::").next().unwrap_or(qual)
}
fn module_of(qual: &str) -> &str {
match qual.rfind("::") {
Some(i) => &qual[..i],
None => "",
}
}
fn has_token(name: &str, lexicon: &[&str]) -> Option<String> {
tokenize(name).into_iter().find(|t| lexicon.contains(&t.as_str()))
}
fn salience(effect: &str) -> i64 {
match effect {
"Net" | "Exec" | "Db" | "Ipc" => 5,
"Fs" | "Env" => 3,
_ => 0,
}
}
fn hops_factor(hops: usize) -> i64 {
match hops {
1 => 2,
2..=4 => 3,
5..=6 => 2,
_ => 1, }
}
pub struct Find {
pub func: String,
pub effect: String,
pub hops: usize,
pub source: String,
pub source_loc: String,
pub benign_token: String,
pub score: i64,
}
fn is_test(qual: &str) -> bool {
let mut segs: Vec<&str> = qual.split("::").collect();
segs.pop(); segs.iter().any(|s| {
let l = s.to_ascii_lowercase();
l == "test" || l == "tests" || s.ends_with("Test") || s.ends_with("Tests")
})
}
fn set_has<E: AsRef<str>>(set: &BTreeSet<E>, e: &str) -> bool {
set.iter().any(|x| x.as_ref() == e)
}
fn nearest_source<'a, E: AsRef<str>>(
func: &'a str,
effect: &str,
direct: &'a HashMap<String, BTreeSet<E>>,
inferred: &'a HashMap<String, BTreeSet<E>>,
calls: &'a HashMap<String, BTreeSet<String>>,
) -> Option<(usize, &'a str)> {
let mut seen: BTreeSet<&str> = BTreeSet::new();
let mut q: VecDeque<(&str, usize)> = VecDeque::new();
seen.insert(func);
q.push_back((func, 0));
while let Some((cur, d)) = q.pop_front() {
if d >= 1 && direct.get(cur).is_some_and(|s| set_has(s, effect)) {
return Some((d, cur));
}
if let Some(cs) = calls.get(cur) {
for c in cs {
let cc = c.as_str();
if !seen.contains(cc) && inferred.get(cc).is_some_and(|s| set_has(s, effect)) {
seen.insert(cc);
q.push_back((cc, d + 1));
}
}
}
}
None
}
pub fn best_finds<E: AsRef<str>>(
inferred: &HashMap<String, BTreeSet<E>>,
direct: &HashMap<String, BTreeSet<E>>,
calls: &HashMap<String, BTreeSet<String>>,
loc: &HashMap<String, String>,
top_n: usize,
) -> Vec<Find> {
let mut quals: Vec<&String> = inferred.keys().collect();
quals.sort();
let mut cands: Vec<Find> = Vec::new();
for f in quals {
let inf = &inferred[f];
if is_test(f) {
continue;
}
let f_leaf = leaf(f);
let f_mod = module_of(f);
if has_token(f_leaf, EFFECTY).is_some() || has_token(f_mod, EFFECTY).is_some() {
continue;
}
let empty = BTreeSet::new();
let dir = direct.get(f).unwrap_or(&empty);
let mut effects: Vec<String> = inf
.iter()
.map(|e| e.as_ref().to_string())
.filter(|e| e != "Unknown" && !set_has(dir, e))
.collect();
effects.sort();
for e in effects {
let sal = salience(&e);
if sal == 0 {
continue;
}
let Some((hops, s)) = nearest_source(f, &e, direct, inferred, calls) else {
continue; };
let benign = has_token(f_leaf, BENIGN);
let benignity = if benign.is_some() { 3 } else { 1 };
let crossing = if module_of(s) != f_mod { 2 } else { 1 };
let score = sal * benignity * hops_factor(hops) * crossing;
if score == 0 {
continue;
}
cands.push(Find {
func: f.clone(),
effect: e,
hops,
source: s.to_string(),
source_loc: loc.get(s).cloned().unwrap_or_default(),
benign_token: benign.unwrap_or_default(),
score,
});
}
}
cands.sort_by(|a, b| {
b.score
.cmp(&a.score)
.then_with(|| a.hops.cmp(&b.hops))
.then_with(|| a.func.cmp(&b.func))
});
let mut seen_fns: BTreeSet<String> = BTreeSet::new();
let mut out: Vec<Find> = Vec::new();
for c in cands {
if out.len() >= top_n {
break;
}
if seen_fns.insert(c.func.clone()) {
out.push(c);
}
}
out
}
pub fn any_effectful<E: AsRef<str>>(inferred: &HashMap<String, BTreeSet<E>>) -> bool {
inferred.values().any(|s| s.iter().any(|e| e.as_ref() != "Unknown"))
}
#[cfg(test)]
mod tests {
use super::*;
fn set(items: &[&'static str]) -> BTreeSet<&'static str> {
items.iter().copied().collect()
}
fn sset(items: &[&str]) -> BTreeSet<String> {
items.iter().map(|s| s.to_string()).collect()
}
#[test]
fn tokenize_splits_all_boundaries() {
assert_eq!(tokenize("settings::Settings::needsUpdate"), vec!["settings", "settings", "needs", "update"]);
assert_eq!(tokenize("api_client::latest_version"), vec!["api", "client", "latest", "version"]);
}
#[test]
fn benign_deep_inherited_beats_shallow_effecty() {
let mut direct: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let mut inferred: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let mut calls: HashMap<String, BTreeSet<String>> = HashMap::new();
let loc: HashMap<String, String> = HashMap::new();
direct.insert("net_layer::do_send".into(), set(&["Net"]));
inferred.insert("net_layer::do_send".into(), set(&["Net"]));
inferred.insert("core::sync_state".into(), set(&["Net"]));
calls.insert("core::sync_state".into(), sset(&["net_layer::do_send"]));
inferred.insert("core::refresh".into(), set(&["Net"]));
calls.insert("core::refresh".into(), sset(&["core::sync_state"]));
inferred.insert("settings::Settings::load".into(), set(&["Net"]));
calls.insert("settings::Settings::load".into(), sset(&["core::refresh"]));
inferred.insert("api::fetch".into(), set(&["Net"]));
calls.insert("api::fetch".into(), sset(&["net_layer::do_send"]));
assert!(any_effectful(&inferred));
let got = best_finds(&inferred, &direct, &calls, &loc, 1);
assert_eq!(got.len(), 1);
assert_eq!(got[0].func, "settings::Settings::load");
assert_eq!(got[0].effect, "Net");
assert_eq!(got[0].hops, 3);
assert_eq!(got[0].source, "net_layer::do_send");
assert_eq!(got[0].benign_token, "load");
}
#[test]
fn dedup_one_row_per_function_top_n() {
let mut direct: HashMap<String, BTreeSet<String>> = HashMap::new();
let mut inferred: HashMap<String, BTreeSet<String>> = HashMap::new();
let mut calls: HashMap<String, BTreeSet<String>> = HashMap::new();
let loc: HashMap<String, String> = HashMap::new();
direct.insert("net_layer::do_send".into(), sset(&["Net"]));
inferred.insert("net_layer::do_send".into(), sset(&["Net"]));
inferred.insert("core::sync_state".into(), sset(&["Net"]));
calls.insert("core::sync_state".into(), sset(&["net_layer::do_send"]));
inferred.insert("core::download_step".into(), sset(&["Net"]));
calls.insert("core::download_step".into(), sset(&["core::sync_state"]));
inferred.insert("settings::Settings::load".into(), sset(&["Net"]));
calls.insert("settings::Settings::load".into(), sset(&["core::download_step"]));
inferred.insert("model::render".into(), sset(&["Net"]));
calls.insert("model::render".into(), sset(&["net_layer::do_send"]));
let got = best_finds(&inferred, &direct, &calls, &loc, 10);
assert_eq!(got.len(), 2, "two distinct benign functions, one row each");
assert_eq!(got[0].func, "settings::Settings::load"); assert_eq!(got[1].func, "model::render");
assert_eq!(best_finds(&inferred, &direct, &calls, &loc, 1).len(), 1);
assert_eq!(got.iter().map(|f| &f.func).collect::<BTreeSet<_>>().len(), got.len());
}
#[test]
fn fallback_when_nothing_qualifies() {
let mut direct: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let mut inferred: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let calls: HashMap<String, BTreeSet<String>> = HashMap::new();
let loc: HashMap<String, String> = HashMap::new();
direct.insert("net::client::send".into(), set(&["Net"]));
inferred.insert("net::client::send".into(), set(&["Net"]));
assert!(best_finds(&inferred, &direct, &calls, &loc, 1).is_empty());
assert!(any_effectful(&inferred), "effectful, so the caller emits the honest fallback");
}
#[test]
fn nothing_when_no_effects() {
let direct: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let mut inferred: HashMap<String, BTreeSet<&'static str>> = HashMap::new();
let calls: HashMap<String, BTreeSet<String>> = HashMap::new();
let loc: HashMap<String, String> = HashMap::new();
inferred.insert("util::parse".into(), set(&["Unknown"]));
assert!(best_finds(&inferred, &direct, &calls, &loc, 1).is_empty());
assert!(!any_effectful(&inferred));
}
}