Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
5.9 kB · 162 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163//! Counts the independent things inside a Rust source file.//!//! A file is read with `syn`, its top-level items become graph nodes, and an//! edge is drawn from A to B when A's body names something B defines. The//! connected components of that graph are the pieces the file could be split//! into without either piece importing from the other. Names that resolve//! outside the file are recorded against the module they come from, which is//! what a split-out file would have to import.
pub mod cluster;pub mod item;pub mod report;pub mod scan;
/// Parse and analyse one file's text.pub fn analyse_source( path: &str, source: &str, peel: usize, limits: cluster::SplitLimits,) -> syn::Result<cluster::Analysis> { Ok(cluster::analyse( path, item::parse(source, limits.explode_impls_over)?, peel, limits, ))}
#[cfg(test)]mod tests { use super::*;
fn clusters(src: &str) -> Vec<Vec<String>> { let a = analyse_source("t.rs", src, 0, Default::default()).expect("fixture parses"); a.clusters .iter() .map(|c| c.items.iter().map(|&i| a.items[i].name.clone()).collect()) .collect() }
#[test] fn unconnected_functions_are_separate_clusters() { let out = clusters("fn a() { let x = 1; }\nfn b() { let y = 2; }\n"); assert_eq!(out.len(), 2); }
#[test] fn a_call_joins_two_functions() { let out = clusters("fn a() { b(); }\nfn b() {}\n"); assert_eq!(out, vec![vec!["a".to_string(), "b".to_string()]]); }
#[test] fn a_test_module_is_its_own_cluster() { let src = r#"fn a() { b(); }fn b() {}#[cfg(test)]mod tests { use super::*; #[test] fn works() { a(); }}"#; let a = analyse_source("t.rs", src, 0, Default::default()).expect("fixture parses"); assert_eq!( a.scores.cluster_count, 1, "tests do not join the code graph" ); let test_clusters: Vec<_> = a.clusters.iter().filter(|c| c.is_test).collect(); assert_eq!(test_clusters.len(), 1); assert!(a.scores.test_lines > 0); }
#[test] fn an_impl_joins_its_type() { let src = "struct Zone;\nimpl Zone { fn name(&self) -> &str { \"z\" } }\nfn other() {}\n"; let out = clusters(src); assert_eq!(out.len(), 2); assert!(out .iter() .any(|c| c.len() == 2 && c.contains(&"Zone".to_string()))); }
#[test] fn a_foreign_trait_impl_stands_alone() { let src = "impl std::fmt::Display for String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } }\nfn a() {}\n"; let a = analyse_source("t.rs", src, 0, Default::default()).expect("fixture parses"); assert_eq!(a.scores.cluster_count, 2); assert!(a.clusters.iter().any(|c| c.is_foreign_impl)); }
#[test] fn imports_follow_the_cluster_that_uses_them() { let src = "use std::collections::HashMap;\nfn a() -> HashMap<u8, u8> { HashMap::new() }\nfn b() {}\n"; let a = analyse_source("t.rs", src, 0, Default::default()).expect("fixture parses"); let user = a .clusters .iter() .find(|c| c.items.iter().any(|&i| a.items[i].name == "a")) .expect("cluster for a"); assert!(user.imports.contains("use std::collections::HashMap;")); let other = a .clusters .iter() .find(|c| c.items.iter().any(|&i| a.items[i].name == "b")) .expect("cluster for b"); assert!(other.imports.is_empty()); }
#[test] fn contiguous_clusters_do_not_count_as_interleaved() { let contiguous = "fn a1() { a2(); }\nfn a2() {}\nfn b1() { b2(); }\nfn b2() {}\n"; let woven = "fn a1() { a2(); }\nfn b1() { b2(); }\nfn a2() {}\nfn b2() {}\n"; let c = analyse_source("t.rs", contiguous, 0, Default::default()).unwrap(); let w = analyse_source("t.rs", woven, 0, Default::default()).unwrap(); assert_eq!(c.scores.cluster_count, 2); assert_eq!(w.scores.cluster_count, 2); assert_eq!(c.scores.interleaving, 0.0); assert!(w.scores.interleaving > 0.0); }
#[test] fn a_thin_link_between_two_halves_makes_two_cohesion_groups() { // One connected component, because `b3` calls `a1` once. The dense // wiring is inside each half, which is what the groups follow. let src = "fn a1() { a2(); a3(); }fn a2() { a1(); a3(); }fn a3() { a1(); a2(); }fn b1() { b2(); b3(); }fn b2() { b1(); b3(); }fn b3() { b1(); b2(); a1(); }"; let a = analyse_source("t.rs", src, 0, Default::default()).unwrap(); assert_eq!(a.scores.cluster_count, 1); assert_eq!(a.scores.group_count, 2); }
#[test] fn a_long_impl_is_read_one_method_at_a_time() { let body = (0..8) .map(|i| format!("fn m{i}(&self) -> u32 {{ {i} }}\n{}", "// pad\n".repeat(12))) .collect::<String>(); let src = format!("struct Big;\nimpl Big {{\n{body}}}\n"); let a = analyse_source("t.rs", &src, 0, Default::default()).unwrap(); let named: Vec<&str> = a.items.iter().map(|i| i.name.as_str()).collect(); assert!(named.contains(&"Big::m0"), "got {named:?}"); assert!(named.contains(&"Big::m7"), "got {named:?}"); }
#[test] fn peeling_a_hub_reveals_the_clusters_behind_it() { let src = "struct Ctx;\nfn a(c: &Ctx) {}\nfn b(c: &Ctx) {}\nfn c_(c: &Ctx) {}\n"; let whole = analyse_source("t.rs", src, 0, Default::default()).unwrap(); assert_eq!(whole.scores.cluster_count, 1); let peeled = analyse_source("t.rs", src, 1, Default::default()).unwrap(); assert_eq!(peeled.peeled.len(), 1); assert_eq!(peeled.scores.cluster_count, 3); }}