use super::{Analyzer, AnalyzerInfo, ProjectAnalyzer, ProjectConfig, ProjectInput};
use crate::services::dead_code_analyzer::{DeadCodeAnalyzer as OriginalAnalyzer, DeadCodeReport};
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::path::Path;
pub struct DeadCodeAnalyzer {
#[allow(dead_code)]
inner: OriginalAnalyzer,
}
impl DeadCodeAnalyzer {
#[must_use]
pub fn new() -> Self {
Self {
inner: OriginalAnalyzer::new(Self::DEFAULT_CAPACITY),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: OriginalAnalyzer::new(capacity),
}
}
const DEFAULT_CAPACITY: usize = 100_000;
}
impl Default for DeadCodeAnalyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeadCodeConfig {
pub base: ProjectConfig,
pub include_unreachable: bool,
pub min_dead_lines: usize,
pub confidence_threshold: f64,
}
impl Default for DeadCodeConfig {
fn default() -> Self {
Self {
base: ProjectConfig::default(),
include_unreachable: true,
min_dead_lines: 5,
confidence_threshold: 0.7,
}
}
}
pub type DeadCodeOutput = DeadCodeReport;
#[async_trait]
impl Analyzer for DeadCodeAnalyzer {
type Input = ProjectInput;
type Output = DeadCodeOutput;
type Config = ProjectConfig;
async fn analyze(&self, input: Self::Input, config: Self::Config) -> Result<Self::Output> {
use crate::services::cargo_dead_code_analyzer::{CargoDeadCodeAnalyzer, DeadCodeKind};
let cargo_analyzer = if config.include_tests {
CargoDeadCodeAnalyzer::new(&input.project_path).include_tests()
} else {
CargoDeadCodeAnalyzer::new(&input.project_path)
};
let accurate_report = cargo_analyzer.analyze().await?;
let mut dead_functions = Vec::new();
let mut dead_classes = Vec::new();
let mut dead_variables = Vec::new();
use crate::models::unified_ast::NodeKey;
use crate::services::dead_code_analyzer::{DeadCodeItem, DeadCodeType};
let mut node_id = 0u32;
for file in &accurate_report.files_with_dead_code {
for item in &file.dead_items {
let dead_item = DeadCodeItem {
node_key: node_id as NodeKey,
name: item.name.clone(),
file_path: file.file_path.display().to_string(),
line_number: item.line as u32,
dead_type: match item.kind {
DeadCodeKind::Function | DeadCodeKind::Method => {
DeadCodeType::UnusedFunction
}
DeadCodeKind::Struct | DeadCodeKind::Enum => DeadCodeType::UnusedClass,
DeadCodeKind::Field | DeadCodeKind::Static | DeadCodeKind::Constant => {
DeadCodeType::UnusedVariable
}
_ => DeadCodeType::UnreachableCode,
},
confidence: 0.95, reason: item.message.clone(),
};
match dead_item.dead_type {
DeadCodeType::UnusedFunction => dead_functions.push(dead_item),
DeadCodeType::UnusedClass => dead_classes.push(dead_item),
DeadCodeType::UnusedVariable => dead_variables.push(dead_item),
_ => {}
}
node_id += 1;
}
}
Ok(DeadCodeReport {
dead_functions,
dead_classes,
dead_variables,
unreachable_code: Vec::new(),
summary: crate::services::dead_code_analyzer::DeadCodeSummary {
total_dead_code_lines: accurate_report.dead_lines,
percentage_dead: accurate_report.dead_code_percentage as f32,
dead_by_type: accurate_report.dead_by_type,
confidence_level: 0.95, },
})
}
fn name(&self) -> &'static str {
"dead_code"
}
}
#[async_trait]
impl ProjectAnalyzer for DeadCodeAnalyzer {
async fn analyze_project(&self, project_path: &Path) -> Result<Self::Output> {
let input = ProjectInput {
project_path: project_path.to_path_buf(),
};
let config = ProjectConfig::default();
self.analyze(input, config).await
}
}
impl AnalyzerInfo for DeadCodeAnalyzer {
fn name(&self) -> &'static str {
"dead_code"
}
fn version(&self) -> &'static str {
env!("CARGO_PKG_VERSION")
}
fn description(&self) -> &'static str {
"Analyzes code for unreachable and unused code patterns"
}
}
pub struct DeadCodeAnalyzerFactory;
impl DeadCodeAnalyzerFactory {
#[must_use]
pub fn create() -> DeadCodeAnalyzer {
DeadCodeAnalyzer::new()
}
#[must_use]
pub fn create_with_capacity(capacity: usize) -> DeadCodeAnalyzer {
DeadCodeAnalyzer::with_capacity(capacity)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dead_code_analyzer_creation() {
let analyzer = DeadCodeAnalyzer::new();
assert_eq!(Analyzer::name(&analyzer), "dead_code");
assert_eq!(Analyzer::version(&analyzer), env!("CARGO_PKG_VERSION"));
}
#[tokio::test]
async fn test_dead_code_config_default() {
let config = DeadCodeConfig::default();
assert!(config.include_unreachable);
assert_eq!(config.min_dead_lines, 5);
assert_eq!(config.confidence_threshold, 0.7);
}
#[tokio::test]
async fn test_analyzer_info() {
let analyzer = DeadCodeAnalyzer::new();
assert_eq!(Analyzer::name(&analyzer), "dead_code");
assert!(AnalyzerInfo::description(&analyzer).contains("unreachable"));
}
#[tokio::test]
async fn test_factory_creation() {
let analyzer = DeadCodeAnalyzerFactory::create();
assert_eq!(Analyzer::name(&analyzer), "dead_code");
let analyzer_with_capacity = DeadCodeAnalyzerFactory::create_with_capacity(50000);
assert_eq!(Analyzer::name(&analyzer_with_capacity), "dead_code");
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}