use super::{count_field_clones, BoilerplateFind};
use crate::config::sections::BoilerplateConfig;
const MIN_CLONE_FIELDS: usize = 3;
pub(super) fn check_clone_heavy_conversion(
parsed: &[(String, String, syn::File)],
config: &BoilerplateConfig,
) -> Vec<BoilerplateFind> {
pattern_guard!("BP-008", config);
parsed
.iter()
.flat_map(|(file, _, syntax)| {
syntax
.items
.iter()
.flat_map(item_fn_bodies)
.filter_map(|(block, line)| clone_heavy_find(block, file, line))
.collect::<Vec<_>>()
})
.collect()
}
fn item_fn_bodies(item: &syn::Item) -> Vec<(&syn::Block, usize)> {
match item {
syn::Item::Fn(f) => vec![(&f.block, f.sig.ident.span().start().line)],
syn::Item::Impl(imp) => imp
.items
.iter()
.filter_map(|sub| match sub {
syn::ImplItem::Fn(m) => Some((&m.block, m.sig.ident.span().start().line)),
_ => None,
})
.collect(),
_ => vec![],
}
}
fn clone_heavy_find(block: &syn::Block, file: &str, line: usize) -> Option<BoilerplateFind> {
block.stmts.iter().find_map(|stmt| {
let clones = count_field_clones(stmt_value(stmt)?);
(clones >= MIN_CLONE_FIELDS).then(|| bp008(file, line, clones))
})
}
fn stmt_value(stmt: &syn::Stmt) -> Option<&syn::Expr> {
match stmt {
syn::Stmt::Expr(e, _) => Some(e),
syn::Stmt::Local(local) => local.init.as_ref().map(|init| &*init.expr),
_ => None,
}
}
fn bp008(file: &str, line: usize, clones: usize) -> BoilerplateFind {
BoilerplateFind {
pattern_id: "BP-008".to_string(),
file: file.to_string(),
line,
struct_name: None,
description: format!(
"Struct construction with {clones} .clone() calls — consider Into/From or ownership transfer"
),
suggestion: "Consider implementing From/Into or restructuring to avoid cloning".to_string(),
suppressed: false,
}
}