use crate::adapters::analyzers::structural::nms::*;
use crate::adapters::analyzers::structural::{StructuralWarning, StructuralWarningKind};
fn detect_in(source: &str) -> Vec<StructuralWarning> {
super::detect_single(source, detect_nms)
}
#[test]
fn needless_mut_self_in_cfg_test_file_excluded() {
let w = detect_in(
"#![cfg(test)]\nstruct S { x: i32 } impl S { fn foo(&mut self) -> i32 { self.x } }",
);
assert!(
w.is_empty(),
"needless &mut self in a #![cfg(test)] file must be excluded: {} warning(s)",
w.len()
);
}
#[test]
fn cfg_test_attr_impl_method_excluded() {
let w = detect_in(
"struct S { x: i32 } #[cfg(test)] impl S { fn foo(&mut self) -> i32 { self.x } }",
);
assert!(
w.is_empty(),
"a #[cfg(test)] impl method must be excluded from NMS: {} warning(s)",
w.len()
);
}
#[test]
fn cfg_test_attr_method_excluded() {
let w = detect_in(
"struct S { x: i32 } impl S { #[cfg(test)] fn foo(&mut self) -> i32 { self.x } }",
);
assert!(
w.is_empty(),
"a #[cfg(test)] method must be excluded from NMS: {} warning(s)",
w.len()
);
}
#[test]
fn test_needless_mut_self_flagged() {
let w = detect_in("struct S { x: i32 } impl S { fn foo(&mut self) -> i32 { self.x } }");
assert_eq!(w.len(), 1);
assert!(matches!(w[0].kind, StructuralWarningKind::NeedlessMutSelf));
}
#[test]
fn test_assignment_not_flagged() {
let w = detect_in("struct S { x: i32 } impl S { fn set(&mut self, v: i32) { self.x = v; } }");
assert!(w.is_empty());
}
#[test]
fn test_method_call_on_self_not_flagged() {
let w = detect_in(
"struct S { items: Vec<i32> } impl S { fn add(&mut self, v: i32) { self.items.push(v); } }",
);
assert!(w.is_empty());
}
#[test]
fn test_mut_borrow_not_flagged() {
let w = detect_in(
"struct S { x: i32 } impl S { fn borrow(&mut self) -> &mut i32 { &mut self.x } }",
);
assert!(w.is_empty());
}
#[test]
fn test_immutable_self_not_checked() {
let w = detect_in("struct S { x: i32 } impl S { fn foo(&self) -> i32 { self.x } }");
assert!(w.is_empty());
}
#[test]
fn test_trait_impl_excluded() {
let w = detect_in("trait T { fn foo(&mut self); } struct S { x: i32 } impl T for S { fn foo(&mut self) { let _ = self.x; } }");
assert!(w.is_empty());
}
#[test]
fn test_no_self_ref_skipped_for_slm() {
let w = detect_in("struct S; impl S { fn foo(&mut self) -> i32 { 42 } }");
assert!(w.is_empty());
}
#[test]
fn test_empty_body_not_flagged() {
let w = detect_in("struct S; impl S { fn foo(&mut self) {} }");
assert!(w.is_empty());
}
#[test]
fn test_indexed_field_method_call_not_flagged() {
let w = detect_in(
"struct S { items: Vec<Vec<i32>> } impl S { fn add(&mut self, i: usize, v: i32) { self.items[i].push(v); } }",
);
assert!(
w.is_empty(),
"self.items[i].push(v) should be recognized as mutation"
);
}
#[test]
fn test_assign_to_local_still_flagged() {
let w = detect_in(
"struct S { x: i32 } impl S { fn f(&mut self) -> i32 { let mut y = 0; y = self.x; y } }",
);
assert_eq!(w.len(), 1, "assigning a local is not a self-mutation");
}
#[test]
fn test_non_assign_binary_on_self_still_flagged() {
let w = detect_in("struct S { x: i32 } impl S { fn f(&mut self) -> bool { self.x > 0 } }");
assert_eq!(w.len(), 1, "a comparison on self.x is not a mutation");
}
#[test]
fn test_immutable_self_reference_still_flagged() {
let w =
detect_in("struct S { x: i32 } impl S { fn f(&mut self) -> i32 { let r = &self.x; *r } }");
assert_eq!(w.len(), 1, "an immutable &self.x borrow is not a mutation");
}
#[test]
fn test_method_call_on_local_still_flagged() {
let w = detect_in(
"struct S { x: i32 } impl S { fn f(&mut self) -> i32 { let v = String::new(); v.len(); self.x } }",
);
assert_eq!(
w.len(),
1,
"a method call on a local is not a self-mutation"
);
}
#[test]
fn test_bare_self_reference_still_flagged() {
let w = detect_in("struct S; impl S { fn f(&mut self) { take(self); } }");
assert_eq!(w.len(), 1, "a bare `self` argument is a self-reference");
}
#[test]
fn test_non_self_path_not_referenced_as_self() {
let w = detect_in("struct S; impl S { fn f(&mut self) { let x = 1; let _ = x; } }");
assert!(
w.is_empty(),
"a non-self local path is not a self-reference"
);
}
#[test]
fn needless_mut_self_in_non_test_module_flagged() {
let w =
detect_in("mod inner { struct S { x: i32 } impl S { fn f(&mut self) -> i32 { self.x } } }");
assert_eq!(
w.len(),
1,
"needless &mut self in a non-test module must be flagged"
);
}
#[test]
fn needless_mut_self_in_inline_cfg_test_module_excluded() {
let w = detect_in(
"fn keep() {} #[cfg(test)] mod tests { struct S { x: i32 } impl S { fn f(&mut self) -> i32 { self.x } } }",
);
assert!(
w.is_empty(),
"needless &mut self in a #[cfg(test)] mod must be excluded"
);
}