use super::{build, never_used_offenses, used_once_offenses};
use crate::abc::parse_vector;
use crate::paths::{Lang, parse_file_lang};
fn parse(src: &'static str) -> crate::dart::DartFile<'static> {
let tree = parse_file_lang(src.as_bytes(), Lang::Dart).expect("dart parses");
build(src.as_bytes(), tree)
}
fn scores(src: &'static str) -> Vec<(String, u32, u32, u32)> {
super::abc::all_scores(&parse(src))
.into_iter()
.map(|o| {
let (a, b, c) = parse_vector(&o.vector);
(o.name, a, b, c)
})
.collect()
}
fn used(src: &'static str) -> Vec<String> {
let mut v: Vec<_> = used_once_offenses(&parse(src))
.into_iter()
.map(|o| o.name)
.collect();
v.sort();
v
}
fn dead(src: &'static str) -> Vec<String> {
let mut v: Vec<_> = never_used_offenses(&parse(src))
.into_iter()
.map(|o| o.name)
.collect();
v.sort();
v
}
#[test]
fn abc_top_level_function_vector() {
assert_eq!(
scores("int usedLater(int a) {\n var once = a + compute(1);\n return once;\n}"),
vec![("usedLater".into(), 1, 2, 0)]
);
}
#[test]
fn abc_class_members_named_and_scored() {
assert_eq!(
scores(
r#"class Cart {
int items = 0;
int get value => items * 2;
void add() {
items.add(this);
print('n');
}
}"#
),
vec![("value".to_string(), 0, 1, 0), ("add".to_string(), 0, 2, 0)]
);
}
#[test]
fn abc_constructors_take_signature_names() {
assert_eq!(
scores(
r#"class Foo {
int x;
Foo(this.x) : x = 3 {
print(x);
}
Foo.named(this.x);
factory Foo.other() => Foo(0);
}"#
),
vec![("Foo".to_string(), 0, 1, 0), ("other".to_string(), 0, 1, 0),]
);
}
#[test]
fn abc_branch_and_guard_family_counts_c() {
let got = scores(
r#"int complicated(bool flag) {
int x = 0;
if (x > 10 && flag) {
x *= 2;
}
for (final i in [1, 2]) {
x += i;
}
switch (x) {
case 1:
break;
default:
break;
}
try {
x = risky(x);
} on Exception catch (err) {
print(err);
}
var pick = flag ? 'y' : 'n';
print(pick);
return x;
}"#,
);
assert_eq!(got.len(), 1);
assert_eq!(got[0].1, 6, "assignments");
assert_eq!(got[0].2, 3, "calls");
assert_eq!(got[0].3, 8, "branches/conditions");
}
#[test]
fn used_once_inline_candidate_for_pure_literal() {
assert_eq!(
used("int f() {\n int dead = 5;\n return dead;\n}"),
vec!["dead"]
);
}
#[test]
fn used_once_spares_impure_rhs_and_compound() {
assert_eq!(
used("int f(int b) {\n var g = compute(b);\n g += 1;\n return g;\n}"),
Vec::<String>::new()
);
}
#[test]
fn used_once_params_are_protocol() {
assert_eq!(used("void f(int p) {\n p = 3;\n}"), Vec::<String>::new());
}
#[test]
fn never_used_reports_locals_but_not_fields_or_protocol() {
assert_eq!(
dead("class K {\n int f0 = 9;\n}\nint m(int q) {\n var lost = 1;\n}"),
vec!["lost", "q"]
);
}
#[test]
fn never_used_interpolated_read_satisfies_use() {
assert_eq!(
dead("void show(int n) {\n print('$n');\n}"),
Vec::<String>::new()
);
}
#[test]
fn closure_capture_of_outer_local_is_a_real_use() {
assert_eq!(
dead(
"void outer() {\n int captured = 1;\n void inner() {\n print(captured);\n }\n inner();\n}"
),
Vec::<String>::new()
);
}
#[test]
fn pattern_destructured_unused_names_reported() {
assert_eq!(
dead("void grab(List<int> xs) {\n final [p, q] = xs;\n}"),
vec!["p", "q"]
);
}
#[test]
fn cascade_member_slots_do_not_shadow_reads() {
assert_eq!(
dead("void go(Obj obj) {\n obj..m1()..f = 2;\n}"),
Vec::<String>::new()
);
}