scribe_analysis/
analyzer.rs

1//! # Code Analysis Engine
2//! 
3//! Placeholder module for semantic code analysis.
4
5use scribe_core::Result;
6use crate::ast::AstNode;
7
8#[derive(Debug, Clone)]
9pub struct AnalysisResult {
10    pub complexity: f64,
11    pub maintainability: f64,
12    pub issues: Vec<String>,
13}
14
15impl AnalysisResult {
16    pub fn new() -> Self {
17        Self {
18            complexity: 0.0,
19            maintainability: 1.0,
20            issues: Vec::new(),
21        }
22    }
23}
24
25impl Default for AnalysisResult {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31pub struct CodeAnalyzer;
32
33impl CodeAnalyzer {
34    pub fn new() -> Self {
35        Self
36    }
37    
38    pub async fn analyze(&self, ast: &AstNode) -> Result<AnalysisResult> {
39        let mut result = AnalysisResult::new();
40        
41        // Calculate cyclomatic complexity from AST structure
42        result.complexity = self.calculate_complexity(ast);
43        
44        // Calculate maintainability index based on complexity and size
45        result.maintainability = self.calculate_maintainability(ast, result.complexity);
46        
47        // Detect common code issues
48        result.issues = self.detect_issues(ast);
49        
50        Ok(result)
51    }
52    
53    fn calculate_complexity(&self, ast: &AstNode) -> f64 {
54        self.complexity_recursive(ast, 1.0)
55    }
56    
57    fn complexity_recursive(&self, node: &AstNode, base_complexity: f64) -> f64 {
58        let mut complexity = base_complexity;
59        
60        // Check node type and add complexity accordingly
61        match node.node_type.as_str() {
62            "if" | "while" | "for" | "match" | "switch" => {
63                complexity += 1.0; // Control flow adds complexity
64            }
65            "function" | "method" => {
66                complexity += 0.5; // Function definitions add some complexity
67            }
68            _ => {
69                // Other nodes don't significantly add complexity
70            }
71        }
72        
73        // Recursively process all children
74        for child in &node.children {
75            complexity += self.complexity_recursive(child, 0.0);
76        }
77        
78        complexity
79    }
80    
81    fn calculate_maintainability(&self, ast: &AstNode, complexity: f64) -> f64 {
82        let size_factor = self.count_nodes(ast) as f64;
83        let nesting_factor = self.max_nesting_depth(ast) as f64;
84        
85        // Maintainability index formula (simplified)
86        // Higher complexity and nesting reduce maintainability
87        let base_maintainability = 100.0;
88        let complexity_penalty = complexity * 5.0;
89        let size_penalty = (size_factor / 50.0) * 10.0;
90        let nesting_penalty = nesting_factor * 15.0;
91        
92        (base_maintainability - complexity_penalty - size_penalty - nesting_penalty)
93            .max(0.0) / 100.0
94    }
95    
96    fn count_nodes(&self, ast: &AstNode) -> usize {
97        let mut count = 1; // Count this node
98        
99        // Count all children recursively
100        for child in &ast.children {
101            count += self.count_nodes(child);
102        }
103        
104        count
105    }
106    
107    fn max_nesting_depth(&self, ast: &AstNode) -> usize {
108        self.nesting_recursive(ast, 0)
109    }
110    
111    fn nesting_recursive(&self, ast: &AstNode, current_depth: usize) -> usize {
112        let mut max_depth = current_depth;
113        
114        // Determine if this node adds nesting depth
115        let next_depth = match ast.node_type.as_str() {
116            "if" | "while" | "for" | "match" | "switch" | "function" | "method" => {
117                current_depth + 1
118            }
119            _ => current_depth
120        };
121        
122        max_depth = max_depth.max(next_depth);
123        
124        // Recursively check all children
125        for child in &ast.children {
126            max_depth = max_depth.max(self.nesting_recursive(child, next_depth));
127        }
128        
129        max_depth
130    }
131    
132    fn detect_issues(&self, ast: &AstNode) -> Vec<String> {
133        let mut issues = Vec::new();
134        
135        let complexity = self.calculate_complexity(ast);
136        if complexity > 10.0 {
137            issues.push(format!("High cyclomatic complexity: {:.1}", complexity));
138        }
139        
140        let nesting = self.max_nesting_depth(ast);
141        if nesting > 4 {
142            issues.push(format!("Deep nesting detected: {} levels", nesting));
143        }
144        
145        let size = self.count_nodes(ast);
146        if size > 100 {
147            issues.push(format!("Large function detected: {} nodes", size));
148        }
149        
150        issues
151    }
152}
153
154impl Default for CodeAnalyzer {
155    fn default() -> Self {
156        Self::new()
157    }
158}