use super::{is_self_field_access, single_return_expr, BoilerplateFind};
use crate::config::sections::BoilerplateConfig;
const MIN_GETTER_SETTER_COUNT: usize = 3;
pub(super) fn check_manual_getter_setter(
parsed: &[(String, String, syn::File)],
config: &BoilerplateConfig,
) -> Vec<BoilerplateFind> {
pattern_guard!("BP-003", config);
parsed
.iter()
.flat_map(|(file, _, syntax)| {
syntax
.items
.iter()
.flat_map(|item| impl_getter_setters(item, file))
.collect::<Vec<_>>()
})
.collect()
}
fn impl_getter_setters(item: &syn::Item, file: &str) -> Vec<BoilerplateFind> {
let syn::Item::Impl(imp) = item else {
return vec![];
};
if imp.trait_.is_some() {
return vec![];
}
let methods: Vec<&syn::ImplItemFn> = imp
.items
.iter()
.filter_map(|i| match i {
syn::ImplItem::Fn(m) => Some(m),
_ => None,
})
.filter(|m| is_getter_or_setter(m))
.collect();
if methods.len() < MIN_GETTER_SETTER_COUNT {
return vec![];
}
let struct_name = impl_struct_name(imp);
methods
.iter()
.map(|m| bp003(file, m.sig.ident.span().start().line, struct_name.clone()))
.collect()
}
fn is_getter_or_setter(m: &syn::ImplItemFn) -> bool {
is_trivial_getter(m) || is_trivial_setter(m)
}
fn is_trivial_getter(m: &syn::ImplItemFn) -> bool {
m.block.stmts.len() == 1
&& m.sig.inputs.len() == 1
&& single_return_expr(&m.block).is_some_and(|expr| {
is_self_field_access(expr)
|| matches!(expr, syn::Expr::Reference(r) if is_self_field_access(&r.expr))
})
}
fn is_trivial_setter(m: &syn::ImplItemFn) -> bool {
m.block.stmts.len() == 1
&& m.sig.inputs.len() == 2
&& matches!(
&m.block.stmts[0],
syn::Stmt::Expr(syn::Expr::Assign(a), Some(_)) if is_self_field_access(&a.left)
)
}
fn impl_struct_name(imp: &syn::ItemImpl) -> Option<String> {
match &*imp.self_ty {
syn::Type::Path(tp) => tp.path.segments.last().map(|s| s.ident.to_string()),
_ => None,
}
}
fn bp003(file: &str, line: usize, struct_name: Option<String>) -> BoilerplateFind {
BoilerplateFind {
pattern_id: "BP-003".to_string(),
file: file.to_string(),
line,
struct_name,
description: "trivial getter/setter — consider field visibility or accessor macro"
.to_string(),
suggestion: "Consider making fields pub or using a getter/setter derive macro".to_string(),
suppressed: false,
}
}