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> {
build(
src.as_bytes(),
parse_file_lang(src.as_bytes(), Lang::Dart).expect("dart parses"),
)
}
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_immediate_call_chain_yes_intervening_and_compound_no() {
assert_eq!(
used("int f(int b) {\n var g = compute(b);\n return g;\n}"),
vec!["g"]
);
assert_eq!(
used("int f(int b) {\n var g = compute(b);\n side();\n return g;\n}"),
Vec::<String>::new()
);
assert_eq!(
used("int f(int b) {\n var g = compute(b);\n g += 1;\n return g;\n}"),
Vec::<String>::new()
);
}
#[test]
fn never_used_dead_call_keeps_initializer() {
let f = never_used_offenses(&parse("int f() {\n var gone = compute(1);\n return 0;\n}"));
assert_eq!(f.len(), 1);
assert_eq!(f[0].name, "gone");
assert!(f[0].keep_init);
}
#[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 never_used_parameter_member_access_is_a_real_use() {
assert_eq!(
dead("class C {\n int m(Obj dto) {\n return dto.x;\n }\n}"),
Vec::<String>::new()
);
assert_eq!(
dead("class C {\n void m(Obj dto) {\n print(dto.images.map((e) => e.x));\n }\n}"),
Vec::<String>::new()
);
}
#[test]
fn never_used_factory_constructor_formals_are_protocol() {
assert_eq!(
dead(
r#"@freezed
class E with _$E {
const factory E.f({required int barcode}) = _F;
}"#,
),
Vec::<String>::new()
);
}
#[test]
fn never_used_constructor_formals_with_body_are_reported() {
assert_eq!(
dead("class Foo {\n int x;\n Foo(int spare, this.x) {\n print(x);\n }\n}"),
vec!["spare"]
);
}
#[test]
fn never_used_unimplemented_stub_formals_are_protocol() {
assert_eq!(
dead("abstract class M {\n int toModel(int dto) => throw UnimplementedError();\n}"),
Vec::<String>::new()
);
}
#[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()
);
}