use crate::code_intelligence::*;
use crate::error::Error;
use crate::thinktool::ThinkToolExecutor;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{info, instrument, warn};
pub struct CodeAnalyzer {
quality_assessors: HashMap<ProgrammingLanguage, Box<dyn QualityAssessor + Send + Sync>>,
pattern_detectors: HashMap<ProgrammingLanguage, Box<dyn PatternDetector + Send + Sync>>,
bug_detectors: HashMap<ProgrammingLanguage, Box<dyn BugDetector + Send + Sync>>,
#[allow(dead_code)]
thinktool_executor: Arc<ThinkToolExecutor>,
}
impl Default for CodeAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl CodeAnalyzer {
pub fn new() -> Self {
let mut quality_assessors = HashMap::new();
let mut pattern_detectors = HashMap::new();
let mut bug_detectors = HashMap::new();
quality_assessors.insert(
ProgrammingLanguage::Rust,
Box::new(RustQualityAssessor::new()) as Box<dyn QualityAssessor + Send + Sync>,
);
quality_assessors.insert(
ProgrammingLanguage::Java,
Box::new(JavaQualityAssessor::new()) as Box<dyn QualityAssessor + Send + Sync>,
);
quality_assessors.insert(
ProgrammingLanguage::Python,
Box::new(PythonQualityAssessor::new()) as Box<dyn QualityAssessor + Send + Sync>,
);
pattern_detectors.insert(
ProgrammingLanguage::Rust,
Box::new(RustPatternDetector::new()) as Box<dyn PatternDetector + Send + Sync>,
);
pattern_detectors.insert(
ProgrammingLanguage::Java,
Box::new(JavaPatternDetector::new()) as Box<dyn PatternDetector + Send + Sync>,
);
pattern_detectors.insert(
ProgrammingLanguage::Python,
Box::new(PythonPatternDetector::new()) as Box<dyn PatternDetector + Send + Sync>,
);
bug_detectors.insert(
ProgrammingLanguage::Rust,
Box::new(RustBugDetector::new()) as Box<dyn BugDetector + Send + Sync>,
);
bug_detectors.insert(
ProgrammingLanguage::Java,
Box::new(JavaBugDetector::new()) as Box<dyn BugDetector + Send + Sync>,
);
bug_detectors.insert(
ProgrammingLanguage::Python,
Box::new(PythonBugDetector::new()) as Box<dyn BugDetector + Send + Sync>,
);
let thinktool_executor = Arc::new(ThinkToolExecutor::new());
Self {
quality_assessors,
pattern_detectors,
bug_detectors,
thinktool_executor,
}
}
#[instrument(skip(self, ast, code))]
pub fn analyze(
&self,
ast: &UnifiedAST,
language: ProgrammingLanguage,
code: &str,
context: Option<CodeAnalysisContext>,
) -> Result<CodeAnalysisResult, Error> {
info!("Performing comprehensive analysis for {:?} code", language);
let quality_metrics = self.assess_quality(ast, language, context.as_ref())?;
let _patterns = self.detect_patterns(ast, language)?;
let bug_findings = self.detect_bugs(ast, language, code)?;
let optimization_suggestions =
self.generate_optimizations(ast, language, &quality_metrics, &bug_findings)?;
let test_suggestions = self.generate_test_suggestions(ast, language, &quality_metrics)?;
let performance_metrics =
self.calculate_performance_metrics(ast, language, &quality_metrics)?;
let cross_language_insights = self.generate_cross_language_insights(ast, language)?;
let complexity_score = self.calculate_complexity_score(ast, &quality_metrics);
Ok(CodeAnalysisResult {
language,
complexity_score,
quality_metrics,
optimization_suggestions,
bug_findings,
test_suggestions,
performance_metrics,
cross_language_insights,
})
}
fn assess_quality(
&self,
ast: &UnifiedAST,
language: ProgrammingLanguage,
context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error> {
if let Some(assessor) = self.quality_assessors.get(&language) {
assessor.assess_quality(ast, context)
} else {
self.assess_generic_quality(ast, context)
}
}
fn assess_generic_quality(
&self,
ast: &UnifiedAST,
_context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error> {
let complexity = &ast.complexity_metrics;
let maintainability_index = self.calculate_maintainability_index(complexity);
let documentation_quality = self.calculate_documentation_quality(ast);
let testability_score = self.calculate_testability_score(ast);
let security_score = self.calculate_security_score(ast);
let performance_score = self.calculate_performance_score(complexity);
Ok(CodeQualityMetrics {
maintainability_index,
cyclomatic_complexity: complexity.cyclomatic_complexity,
code_coverage_potential: self.calculate_coverage_potential(ast),
documentation_quality,
testability_score,
security_score,
performance_score,
})
}
fn detect_patterns(
&self,
ast: &UnifiedAST,
language: ProgrammingLanguage,
) -> Result<Vec<PatternFinding>, Error> {
if let Some(detector) = self.pattern_detectors.get(&language) {
detector.detect_patterns(ast)
} else {
self.detect_generic_patterns(ast)
}
}
fn detect_generic_patterns(&self, ast: &UnifiedAST) -> Result<Vec<PatternFinding>, Error> {
let mut patterns = Vec::new();
if ast.complexity_metrics.cyclomatic_complexity > 10.0 {
patterns.push(PatternFinding {
pattern_type: PatternType::AntiPattern,
name: "High Cyclomatic Complexity".to_string(),
description: "Function has high cyclomatic complexity, consider refactoring"
.to_string(),
severity: PatternSeverity::Medium,
location: None,
});
}
if ast.complexity_metrics.nesting_depth > 4 {
patterns.push(PatternFinding {
pattern_type: PatternType::AntiPattern,
name: "Deep Nesting".to_string(),
description: "Code has deep nesting levels, consider flattening".to_string(),
severity: PatternSeverity::Medium,
location: None,
});
}
Ok(patterns)
}
fn detect_bugs(
&self,
ast: &UnifiedAST,
language: ProgrammingLanguage,
code: &str,
) -> Result<Vec<BugFinding>, Error> {
if let Some(detector) = self.bug_detectors.get(&language) {
detector.detect_bugs(ast, code)
} else {
self.detect_generic_bugs(ast, code)
}
}
fn detect_generic_bugs(&self, ast: &UnifiedAST, code: &str) -> Result<Vec<BugFinding>, Error> {
let mut bugs = Vec::new();
if code.contains("eval(") || code.contains("exec(") {
bugs.push(BugFinding {
severity: BugSeverity::High,
category: BugCategory::Security,
description: "Use of eval/exec can lead to code injection vulnerabilities"
.to_string(),
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
confidence: 0.9,
suggested_fix: Some(
"Use safer alternatives like JSON parsing or input validation".to_string(),
),
});
}
if ast.variables.iter().any(|v| v.data_type.is_none()) {
bugs.push(BugFinding {
severity: BugSeverity::Medium,
category: BugCategory::Type,
description:
"Variables without explicit type declarations may cause runtime errors"
.to_string(),
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
confidence: 0.7,
suggested_fix: Some(
"Add explicit type declarations for better type safety".to_string(),
),
});
}
Ok(bugs)
}
fn generate_optimizations(
&self,
_ast: &UnifiedAST,
_language: ProgrammingLanguage,
quality_metrics: &CodeQualityMetrics,
bug_findings: &[BugFinding],
) -> Result<Vec<OptimizationSuggestion>, Error> {
let mut suggestions = Vec::new();
if quality_metrics.performance_score < 0.7 {
suggestions.push(OptimizationSuggestion {
category: OptimizationCategory::Performance,
priority: SuggestionPriority::High,
description: "Optimize performance-critical sections".to_string(),
impact: 0.8,
effort: 0.6,
code_example: Some("Consider algorithmic optimizations or caching".to_string()),
});
}
let security_bugs = bug_findings
.iter()
.filter(|b| b.category == BugCategory::Security)
.count();
if security_bugs > 0 {
suggestions.push(OptimizationSuggestion {
category: OptimizationCategory::Security,
priority: SuggestionPriority::Critical,
description: format!("Address {} security vulnerabilities", security_bugs),
impact: 0.9,
effort: 0.7,
code_example: Some(
"Implement input validation and secure coding practices".to_string(),
),
});
}
if quality_metrics.maintainability_index < 0.7 {
suggestions.push(OptimizationSuggestion {
category: OptimizationCategory::Maintainability,
priority: SuggestionPriority::Medium,
description: "Improve code maintainability through refactoring".to_string(),
impact: 0.6,
effort: 0.4,
code_example: Some(
"Extract methods, reduce complexity, improve naming".to_string(),
),
});
}
if quality_metrics.testability_score < 0.6 {
suggestions.push(OptimizationSuggestion {
category: OptimizationCategory::Testing,
priority: SuggestionPriority::High,
description: "Improve code testability".to_string(),
impact: 0.7,
effort: 0.3,
code_example: Some(
"Use dependency injection, reduce coupling, increase cohesion".to_string(),
),
});
}
Ok(suggestions)
}
fn generate_test_suggestions(
&self,
ast: &UnifiedAST,
_language: ProgrammingLanguage,
quality_metrics: &CodeQualityMetrics,
) -> Result<Vec<TestSuggestion>, Error> {
let mut suggestions = Vec::new();
for function in &ast.functions {
suggestions.push(TestSuggestion {
test_type: TestType::Unit,
description: format!("Unit tests for function '{}'", function.name),
target_function: Some(function.name.clone()),
coverage_area: "Function behavior".to_string(),
priority: SuggestionPriority::High,
});
if function.complexity.cyclomatic_complexity > 5.0 {
suggestions.push(TestSuggestion {
test_type: TestType::EdgeCase,
description: format!("Edge case tests for function '{}'", function.name),
target_function: Some(function.name.clone()),
coverage_area: "Boundary conditions".to_string(),
priority: SuggestionPriority::Medium,
});
}
}
if !ast.classes.is_empty() {
suggestions.push(TestSuggestion {
test_type: TestType::Integration,
description: "Integration tests for class interactions".to_string(),
target_function: None,
coverage_area: "Class integration".to_string(),
priority: SuggestionPriority::Medium,
});
}
if quality_metrics.cyclomatic_complexity > 8.0 {
suggestions.push(TestSuggestion {
test_type: TestType::Performance,
description: "Performance tests for high-complexity code".to_string(),
target_function: None,
coverage_area: "Performance characteristics".to_string(),
priority: SuggestionPriority::Low,
});
}
Ok(suggestions)
}
fn calculate_performance_metrics(
&self,
ast: &UnifiedAST,
_language: ProgrammingLanguage,
_quality_metrics: &CodeQualityMetrics,
) -> Result<PerformanceMetrics, Error> {
let complexity = &ast.complexity_metrics;
let execution_time_estimate =
complexity.cyclomatic_complexity * complexity.lines_of_code as f64 / 100.0;
let memory_usage_estimate = complexity.lines_of_code as f64 * 0.1;
let cpu_intensity = (complexity.cyclomatic_complexity / 10.0).min(1.0);
let scalability_score = (1.0 - (complexity.cyclomatic_complexity / 20.0)).max(0.0);
let mut bottlenecks = Vec::new();
if complexity.cyclomatic_complexity > 10.0 {
bottlenecks.push(PerformanceBottleneck {
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
impact: 0.8,
description: "High cyclomatic complexity may cause performance issues".to_string(),
potential_optimization: "Consider algorithmic optimization or code simplification"
.to_string(),
});
}
if complexity.nesting_depth > 4 {
bottlenecks.push(PerformanceBottleneck {
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
impact: 0.6,
description: "Deep nesting can impact performance".to_string(),
potential_optimization: "Flatten nested structures or use early returns"
.to_string(),
});
}
Ok(PerformanceMetrics {
execution_time_estimate,
memory_usage_estimate,
cpu_intensity,
scalability_score,
bottlenecks,
})
}
fn generate_cross_language_insights(
&self,
_ast: &UnifiedAST,
language: ProgrammingLanguage,
) -> Result<Vec<CrossLanguageInsight>, Error> {
let mut insights = Vec::new();
match language {
ProgrammingLanguage::Rust => {
insights.push(CrossLanguageInsight {
source_language: language,
target_language: ProgrammingLanguage::Java,
insight_type: CrossLanguageInsightType::BestPractice,
description:
"Rust's ownership model prevents memory leaks that Java avoids through GC"
.to_string(),
potential_improvements: vec![
"Consider RAII patterns in other languages".to_string()
],
});
}
ProgrammingLanguage::Java => {
insights.push(CrossLanguageInsight {
source_language: language,
target_language: ProgrammingLanguage::Python,
insight_type: CrossLanguageInsightType::Pattern,
description: "Java's interface patterns can improve Python code organization"
.to_string(),
potential_improvements: vec!["Use abstract base classes in Python".to_string()],
});
}
_ => {}
}
Ok(insights)
}
fn calculate_complexity_score(
&self,
ast: &UnifiedAST,
quality_metrics: &CodeQualityMetrics,
) -> f64 {
let cyclomatic_weight = 0.3;
let cognitive_weight = 0.2;
let nesting_weight = 0.2;
let maintainability_weight = 0.3;
let cyclomatic_score =
(20.0 - ast.complexity_metrics.cyclomatic_complexity).max(0.0) / 20.0;
let cognitive_score = (20.0 - ast.complexity_metrics.cognitive_complexity).max(0.0) / 20.0;
let nesting_score = (5.0 - ast.complexity_metrics.nesting_depth as f64).max(0.0) / 5.0;
let maintainability_score = quality_metrics.maintainability_index;
cyclomatic_score * cyclomatic_weight
+ cognitive_score * cognitive_weight
+ nesting_score * nesting_weight
+ maintainability_score * maintainability_weight
}
fn calculate_maintainability_index(&self, complexity: &ComplexityMetrics) -> f64 {
let halstead = &complexity.halstead_complexity;
let volume_score = if halstead.volume > 0.0 {
(171.0
- 5.2 * halstead.volume.log2()
- 0.23 * complexity.cyclomatic_complexity
- 16.2 * (complexity.lines_of_code as f64).log2())
.max(0.0)
} else {
100.0
};
(volume_score / 171.0).min(1.0)
}
fn calculate_documentation_quality(&self, ast: &UnifiedAST) -> f64 {
let total_functions = ast.functions.len() + ast.classes.len();
if total_functions == 0 {
return 1.0;
}
let documented_items = ast
.comments
.iter()
.filter(|c| c.comment_type == CommentType::Documentation)
.count();
(documented_items as f64 / total_functions as f64).min(1.0)
}
fn calculate_testability_score(&self, ast: &UnifiedAST) -> f64 {
let mut score = 1.0;
score -= (ast.complexity_metrics.cyclomatic_complexity / 20.0).min(0.5);
score -= (ast.complexity_metrics.nesting_depth as f64 / 10.0).min(0.3);
let avg_function_lines = if !ast.functions.is_empty() {
ast.functions
.iter()
.map(|f| f.complexity.lines_of_code)
.sum::<u32>() as f64
/ ast.functions.len() as f64
} else {
0.0
};
if avg_function_lines > 50.0 {
score -= 0.2;
}
score.clamp(0.0f64, 1.0)
}
fn calculate_security_score(&self, ast: &UnifiedAST) -> f64 {
let mut score: f64 = 1.0;
let security_keywords = ["eval", "exec", "system", "shell_exec"];
let code_text = format!(
"{} {} {} {}",
ast.functions
.iter()
.map(|f| f.name.as_str())
.collect::<Vec<_>>()
.join(" "),
ast.variables
.iter()
.map(|v| v.name.as_str())
.collect::<Vec<_>>()
.join(" "),
ast.classes
.iter()
.map(|c| c.name.as_str())
.collect::<Vec<_>>()
.join(" "),
ast.comments
.iter()
.map(|c| c.content.as_str())
.collect::<Vec<_>>()
.join(" ")
);
for keyword in &security_keywords {
if code_text.contains(keyword) {
score -= 0.1;
}
}
score.clamp(0.0f64, 1.0)
}
fn calculate_performance_score(&self, complexity: &ComplexityMetrics) -> f64 {
let mut score: f64 = 1.0;
score -= (complexity.cyclomatic_complexity / 20.0).min(0.4);
score -= (complexity.nesting_depth as f64 / 10.0).min(0.3);
if complexity.lines_of_code > 100 {
score -= 0.2;
}
score.clamp(0.0f64, 1.0)
}
fn calculate_coverage_potential(&self, ast: &UnifiedAST) -> f64 {
let total_functions = ast.functions.len() + ast.classes.len();
if total_functions == 0 {
return 0.0;
}
let testable_functions = ast
.functions
.iter()
.filter(|f| !f.parameters.is_empty())
.count();
(testable_functions as f64 / total_functions as f64).min(1.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PatternFinding {
pub pattern_type: PatternType,
pub name: String,
pub description: String,
pub severity: PatternSeverity,
pub location: Option<CodeLocation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PatternType {
DesignPattern,
AntiPattern,
CodeSmell,
BestPractice,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PatternSeverity {
Critical,
High,
Medium,
Low,
}
#[async_trait]
pub trait QualityAssessor: Send + Sync {
fn assess_quality(
&self,
ast: &UnifiedAST,
context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error>;
}
#[async_trait]
pub trait PatternDetector: Send + Sync {
fn detect_patterns(&self, ast: &UnifiedAST) -> Result<Vec<PatternFinding>, Error>;
}
#[async_trait]
pub trait BugDetector: Send + Sync {
fn detect_bugs(&self, ast: &UnifiedAST, code: &str) -> Result<Vec<BugFinding>, Error>;
}
pub struct RustQualityAssessor;
impl RustQualityAssessor {
pub fn new() -> Self {
Self
}
}
impl Default for RustQualityAssessor {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl QualityAssessor for RustQualityAssessor {
fn assess_quality(
&self,
ast: &UnifiedAST,
_context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error> {
let mut metrics = CodeQualityMetrics::default();
let has_error_handling = ast
.functions
.iter()
.any(|f| f.body.iter().any(|s| s.content.contains("Result")));
let has_ownership_patterns = ast.variables.iter().any(|v| !v.is_mutable);
if has_error_handling {
metrics.security_score += 0.1;
}
if has_ownership_patterns {
metrics.performance_score += 0.1;
metrics.maintainability_index += 0.1;
}
metrics.security_score = (metrics.security_score + 0.2).min(1.0);
Ok(metrics)
}
}
pub struct RustPatternDetector;
impl RustPatternDetector {
pub fn new() -> Self {
Self
}
}
impl Default for RustPatternDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl PatternDetector for RustPatternDetector {
fn detect_patterns(&self, ast: &UnifiedAST) -> Result<Vec<PatternFinding>, Error> {
let mut patterns = Vec::new();
for function in &ast.functions {
if function.body.iter().any(|s| s.content.contains("unwrap()")) {
patterns.push(PatternFinding {
pattern_type: PatternType::CodeSmell,
name: "Unchecked unwrap()".to_string(),
description: "Consider using proper error handling instead of unwrap()"
.to_string(),
severity: PatternSeverity::Medium,
location: Some(CodeLocation {
file_path: "unknown".to_string(),
line_number: Some(function.line_number),
column_number: None,
function_name: Some(function.name.clone()),
}),
});
}
}
Ok(patterns)
}
}
pub struct RustBugDetector;
impl RustBugDetector {
pub fn new() -> Self {
Self
}
}
impl Default for RustBugDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl BugDetector for RustBugDetector {
fn detect_bugs(&self, _ast: &UnifiedAST, code: &str) -> Result<Vec<BugFinding>, Error> {
let mut bugs = Vec::new();
if code.contains("unsafe ") {
bugs.push(BugFinding {
severity: BugSeverity::Medium,
category: BugCategory::Memory,
description: "Unsafe code block detected - ensure proper safety checks".to_string(),
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
confidence: 0.8,
suggested_fix: Some(
"Add safety invariants and consider if unsafe is truly necessary".to_string(),
),
});
}
Ok(bugs)
}
}
pub struct JavaQualityAssessor;
impl JavaQualityAssessor {
pub fn new() -> Self {
Self
}
}
impl Default for JavaQualityAssessor {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl QualityAssessor for JavaQualityAssessor {
fn assess_quality(
&self,
ast: &UnifiedAST,
_context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error> {
let mut metrics = CodeQualityMetrics::default();
let has_inheritance = ast.classes.iter().any(|c| !c.inheritance.is_empty());
let has_interfaces = ast.classes.iter().any(|c| !c.interfaces.is_empty());
if has_inheritance {
metrics.maintainability_index += 0.1;
}
if has_interfaces {
metrics.testability_score += 0.1;
}
Ok(metrics)
}
}
pub struct JavaPatternDetector;
impl JavaPatternDetector {
pub fn new() -> Self {
Self
}
}
impl Default for JavaPatternDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl PatternDetector for JavaPatternDetector {
fn detect_patterns(&self, ast: &UnifiedAST) -> Result<Vec<PatternFinding>, Error> {
let mut patterns = Vec::new();
for class in &ast.classes {
if class.methods.len() > 20 {
patterns.push(PatternFinding {
pattern_type: PatternType::AntiPattern,
name: "God Class".to_string(),
description: format!(
"Class '{}' has too many methods ({})",
class.name,
class.methods.len()
),
severity: PatternSeverity::High,
location: Some(CodeLocation {
file_path: "unknown".to_string(),
line_number: Some(class.line_number),
column_number: None,
function_name: None,
}),
});
}
}
Ok(patterns)
}
}
pub struct JavaBugDetector;
impl JavaBugDetector {
pub fn new() -> Self {
Self
}
}
impl Default for JavaBugDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl BugDetector for JavaBugDetector {
fn detect_bugs(&self, _ast: &UnifiedAST, code: &str) -> Result<Vec<BugFinding>, Error> {
let mut bugs = Vec::new();
if code.contains("System.out.println") {
bugs.push(BugFinding {
severity: BugSeverity::Low,
category: BugCategory::Logic,
description: "Debug print statement detected in production code".to_string(),
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
confidence: 0.9,
suggested_fix: Some(
"Use proper logging framework instead of System.out.println".to_string(),
),
});
}
Ok(bugs)
}
}
pub struct PythonQualityAssessor;
impl PythonQualityAssessor {
pub fn new() -> Self {
Self
}
}
impl Default for PythonQualityAssessor {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl QualityAssessor for PythonQualityAssessor {
fn assess_quality(
&self,
ast: &UnifiedAST,
_context: Option<&CodeAnalysisContext>,
) -> Result<CodeQualityMetrics, Error> {
let mut metrics = CodeQualityMetrics::default();
let has_list_comprehensions = ast
.functions
.iter()
.any(|f| f.body.iter().any(|s| s.content.contains("[")));
let has_context_managers = ast
.functions
.iter()
.any(|f| f.body.iter().any(|s| s.content.contains("with ")));
if has_list_comprehensions {
metrics.performance_score += 0.1;
}
if has_context_managers {
metrics.security_score += 0.1;
metrics.maintainability_index += 0.1;
}
Ok(metrics)
}
}
pub struct PythonPatternDetector;
impl PythonPatternDetector {
pub fn new() -> Self {
Self
}
}
impl Default for PythonPatternDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl PatternDetector for PythonPatternDetector {
fn detect_patterns(&self, ast: &UnifiedAST) -> Result<Vec<PatternFinding>, Error> {
let mut patterns = Vec::new();
for function in &ast.functions {
if function.body.len() > 50 {
patterns.push(PatternFinding {
pattern_type: PatternType::CodeSmell,
name: "Long Function".to_string(),
description: format!(
"Function '{}' is very long ({} statements)",
function.name,
function.body.len()
),
severity: PatternSeverity::Medium,
location: Some(CodeLocation {
file_path: "unknown".to_string(),
line_number: Some(function.line_number),
column_number: None,
function_name: Some(function.name.clone()),
}),
});
}
}
Ok(patterns)
}
}
pub struct PythonBugDetector;
impl PythonBugDetector {
pub fn new() -> Self {
Self
}
}
impl Default for PythonBugDetector {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl BugDetector for PythonBugDetector {
fn detect_bugs(&self, _ast: &UnifiedAST, code: &str) -> Result<Vec<BugFinding>, Error> {
let mut bugs = Vec::new();
if code.contains("except:") {
bugs.push(BugFinding {
severity: BugSeverity::High,
category: BugCategory::Logic,
description: "Bare except clause detected - catch specific exceptions".to_string(),
location: CodeLocation {
file_path: "unknown".to_string(),
line_number: None,
column_number: None,
function_name: None,
},
confidence: 0.9,
suggested_fix: Some("Specify the exceptions you want to catch".to_string()),
});
}
Ok(bugs)
}
}
impl Default for CodeQualityMetrics {
fn default() -> Self {
Self {
maintainability_index: 0.8,
cyclomatic_complexity: 1.0,
code_coverage_potential: 0.8,
documentation_quality: 0.7,
testability_score: 0.8,
security_score: 0.9,
performance_score: 0.8,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_code_analyzer_creation() {
let analyzer = CodeAnalyzer::new();
assert!(!analyzer.quality_assessors.is_empty());
assert!(!analyzer.pattern_detectors.is_empty());
assert!(!analyzer.bug_detectors.is_empty());
}
#[test]
fn test_complexity_score_calculation() {
let analyzer = CodeAnalyzer::new();
let ast = UnifiedAST {
language: ProgrammingLanguage::Rust,
functions: vec![],
classes: vec![],
variables: vec![],
imports: vec![],
comments: vec![],
complexity_metrics: ComplexityMetrics {
cyclomatic_complexity: 5.0,
cognitive_complexity: 6.0,
nesting_depth: 2,
lines_of_code: 20,
halstead_complexity: HalsteadMetrics {
operators: HashMap::new(),
operands: HashMap::new(),
vocabulary: 10,
length: 20,
volume: 86.4,
difficulty: 5.0,
},
},
};
let quality_metrics = CodeQualityMetrics {
maintainability_index: 0.8,
cyclomatic_complexity: 5.0,
code_coverage_potential: 0.7,
documentation_quality: 0.6,
testability_score: 0.8,
security_score: 0.9,
performance_score: 0.8,
};
let complexity_score = analyzer.calculate_complexity_score(&ast, &quality_metrics);
assert!((0.0..=1.0).contains(&complexity_score));
}
}