1use crate::tools_legacy::{CallToolParams, CallToolResult, Tool, ToolContent};
4use crate::CodePrismMcpServer;
5use anyhow::Result;
6use codeprism_analysis::security::SecurityAnalyzer;
7use serde_json::Value;
8use std::path::Path;
9
10pub fn list_tools() -> Vec<Tool> {
12 vec![
13 Tool {
14 name: "find_duplicates".to_string(),
15 title: Some("Find Code Duplicates".to_string()),
16 description: "Detect duplicate code patterns and similar code blocks".to_string(),
17 input_schema: serde_json::json!({
18 "type": "object",
19 "properties": {
20 "similarity_threshold": {
21 "type": "number",
22 "description": "Similarity threshold for detecting duplicates (0.0 to 1.0)",
23 "default": 0.8,
24 "minimum": 0.0,
25 "maximum": 1.0
26 },
27 "min_lines": {
28 "type": "number",
29 "description": "Minimum number of lines for a duplicate block",
30 "default": 3,
31 "minimum": 1
32 },
33 "scope": {
34 "type": "string",
35 "description": "Scope for duplicate detection",
36 "default": "repository"
37 }
38 },
39 "required": []
40 }),
41 },
42 Tool {
43 name: "find_unused_code".to_string(),
44 title: Some("Find Unused Code".to_string()),
45 description: "Identify unused functions, classes, variables, and imports".to_string(),
46 input_schema: serde_json::json!({
47 "type": "object",
48 "properties": {
49 "scope": {
50 "type": "string",
51 "description": "Scope for unused code analysis",
52 "default": "repository"
53 },
54 "analyze_types": {
55 "type": "array",
56 "items": {
57 "type": "string",
58 "enum": ["functions", "classes", "variables", "imports", "all"]
59 },
60 "description": "Types of code elements to analyze",
61 "default": ["functions", "classes", "variables", "imports"]
62 },
63 "confidence_threshold": {
64 "type": "number",
65 "description": "Confidence threshold for unused detection",
66 "default": 0.7,
67 "minimum": 0.0,
68 "maximum": 1.0
69 },
70 "consider_external_apis": {
71 "type": "boolean",
72 "description": "Consider external APIs in analysis",
73 "default": true
74 },
75 "include_dead_code": {
76 "type": "boolean",
77 "description": "Include dead code blocks in analysis",
78 "default": true
79 },
80 "exclude_patterns": {
81 "type": "array",
82 "items": {
83 "type": "string"
84 },
85 "description": "Patterns to exclude from analysis",
86 "default": []
87 }
88 },
89 "required": []
90 }),
91 },
92 Tool {
93 name: "analyze_security".to_string(),
94 title: Some("Analyze Security Vulnerabilities".to_string()),
95 description: "Identify security vulnerabilities and potential threats".to_string(),
96 input_schema: serde_json::json!({
97 "type": "object",
98 "properties": {
99 "scope": {
100 "type": "string",
101 "description": "Scope for security analysis",
102 "default": "repository"
103 },
104 "vulnerability_types": {
105 "type": "array",
106 "items": {
107 "type": "string",
108 "enum": ["injection", "xss", "csrf", "authentication", "authorization", "data_exposure", "unsafe_patterns", "crypto", "all"]
109 },
110 "description": "Types of vulnerabilities to check",
111 "default": ["injection", "xss", "csrf", "authentication"]
112 },
113 "severity_threshold": {
114 "type": "string",
115 "enum": ["low", "medium", "high", "critical"],
116 "description": "Minimum severity level to report",
117 "default": "medium"
118 }
119 },
120 "required": []
121 }),
122 },
123 Tool {
124 name: "analyze_performance".to_string(),
125 title: Some("Analyze Performance Issues".to_string()),
126 description: "Identify performance bottlenecks and optimization opportunities"
127 .to_string(),
128 input_schema: serde_json::json!({
129 "type": "object",
130 "properties": {
131 "scope": {
132 "type": "string",
133 "description": "Scope for performance analysis",
134 "default": "repository"
135 },
136 "analysis_types": {
137 "type": "array",
138 "items": {
139 "type": "string",
140 "enum": ["time_complexity", "memory_usage", "hot_spots", "anti_patterns", "scalability", "all"]
141 },
142 "description": "Types of performance analysis to perform",
143 "default": ["time_complexity", "memory_usage", "hot_spots"]
144 },
145 "complexity_threshold": {
146 "type": "string",
147 "enum": ["low", "medium", "high"],
148 "description": "Complexity threshold for reporting issues",
149 "default": "medium"
150 },
151 "include_algorithmic_analysis": {
152 "type": "boolean",
153 "description": "Include algorithmic analysis in performance analysis",
154 "default": true
155 },
156 "detect_bottlenecks": {
157 "type": "boolean",
158 "description": "Detect performance bottlenecks",
159 "default": true
160 },
161 "exclude_patterns": {
162 "type": "array",
163 "items": {
164 "type": "string"
165 },
166 "description": "Patterns to exclude from performance analysis",
167 "default": []
168 }
169 },
170 "required": []
171 }),
172 },
173 Tool {
174 name: "analyze_api_surface".to_string(),
175 title: Some("Analyze API Surface".to_string()),
176 description: "Analyze public API surface, versioning, and breaking changes".to_string(),
177 input_schema: serde_json::json!({
178 "type": "object",
179 "properties": {
180 "scope": {
181 "type": "string",
182 "description": "Scope for API surface analysis",
183 "default": "repository"
184 },
185 "analysis_types": {
186 "type": "array",
187 "items": {
188 "type": "string",
189 "enum": ["public_api", "versioning", "breaking_changes", "documentation_coverage", "compatibility", "all"]
190 },
191 "description": "Types of API analysis to perform",
192 "default": ["public_api", "versioning", "breaking_changes"]
193 },
194 "include_private_apis": {
195 "type": "boolean",
196 "description": "Include private APIs in analysis",
197 "default": false
198 },
199 "api_version": {
200 "type": "string",
201 "description": "API version for compatibility analysis",
202 "default": ""
203 },
204 "check_documentation_coverage": {
205 "type": "boolean",
206 "description": "Check API documentation coverage",
207 "default": true
208 },
209 "detect_breaking_changes": {
210 "type": "boolean",
211 "description": "Detect API breaking changes",
212 "default": true
213 },
214 "exclude_patterns": {
215 "type": "array",
216 "items": {
217 "type": "string"
218 },
219 "description": "Patterns to exclude from API surface analysis",
220 "default": []
221 }
222 },
223 "required": []
224 }),
225 },
226 ]
227}
228
229pub async fn call_tool(
231 server: &CodePrismMcpServer,
232 params: &CallToolParams,
233) -> Result<CallToolResult> {
234 match params.name.as_str() {
235 "find_duplicates" => find_duplicates(server, params.arguments.as_ref()).await,
236 "find_unused_code" => find_unused_code(server, params.arguments.as_ref()).await,
237 "analyze_security" => analyze_security(server, params.arguments.as_ref()).await,
238 "analyze_performance" => analyze_performance(server, params.arguments.as_ref()).await,
239 "analyze_api_surface" => analyze_api_surface(server, params.arguments.as_ref()).await,
240 _ => Err(anyhow::anyhow!(
241 "Unknown quality analysis tool: {}",
242 params.name
243 )),
244 }
245}
246
247async fn find_duplicates(
249 _server: &CodePrismMcpServer,
250 arguments: Option<&Value>,
251) -> Result<CallToolResult> {
252 let default_args = serde_json::json!({});
253 let args = arguments.unwrap_or(&default_args);
254
255 let similarity_threshold = args
256 .get("similarity_threshold")
257 .and_then(|v| v.as_f64())
258 .unwrap_or(0.8);
259
260 let min_lines = args
261 .get("min_lines")
262 .and_then(|v| v.as_u64())
263 .map(|v| v as usize)
264 .unwrap_or(3);
265
266 let scope = args
267 .get("scope")
268 .and_then(|v| v.as_str())
269 .unwrap_or("repository");
270
271 let result = serde_json::json!({
272 "scope": scope,
273 "parameters": {
274 "similarity_threshold": similarity_threshold,
275 "min_lines": min_lines
276 },
277 "duplicates_found": 0,
278 "summary": "Duplicate detection analysis completed - no duplicates found",
279 "status": "placeholder_implementation"
280 });
281
282 Ok(CallToolResult {
283 content: vec![ToolContent::Text {
284 text: serde_json::to_string_pretty(&result)?,
285 }],
286 is_error: Some(false),
287 })
288}
289
290async fn find_unused_code(
292 server: &CodePrismMcpServer,
293 arguments: Option<&Value>,
294) -> Result<CallToolResult> {
295 let default_args = serde_json::json!({});
296 let args = arguments.unwrap_or(&default_args);
297
298 let scope = args
299 .get("scope")
300 .and_then(|v| v.as_str())
301 .unwrap_or("repository");
302
303 let analyze_types = args
304 .get("analyze_types")
305 .and_then(|v| v.as_array())
306 .map(|arr| {
307 arr.iter()
308 .filter_map(|v| v.as_str().map(|s| s.to_string()))
309 .collect::<Vec<_>>()
310 })
311 .unwrap_or_else(|| {
312 vec![
313 "functions".to_string(),
314 "classes".to_string(),
315 "variables".to_string(),
316 "imports".to_string(),
317 ]
318 });
319
320 let confidence_threshold = args
321 .get("confidence_threshold")
322 .and_then(|v| v.as_f64())
323 .unwrap_or(0.7);
324
325 let consider_external_apis = args
326 .get("consider_external_apis")
327 .and_then(|v| v.as_bool())
328 .unwrap_or(true);
329
330 let include_dead_code = args
331 .get("include_dead_code")
332 .and_then(|v| v.as_bool())
333 .unwrap_or(true);
334
335 let exclude_patterns = args
336 .get("exclude_patterns")
337 .and_then(|v| v.as_array())
338 .map(|arr| {
339 arr.iter()
340 .filter_map(|v| v.as_str().map(|s| s.to_string()))
341 .collect::<Vec<_>>()
342 })
343 .unwrap_or_default();
344
345 let mut unused_functions = Vec::new();
347 let mut unused_classes = Vec::new();
348 let mut unused_variables = Vec::new();
349 let mut unused_imports = Vec::new();
350 let mut dead_code_blocks = Vec::new();
351
352 if analyze_types.contains(&"functions".to_string())
354 || analyze_types.contains(&"all".to_string())
355 {
356 unused_functions = find_unused_functions(
357 server,
358 confidence_threshold,
359 consider_external_apis,
360 &exclude_patterns,
361 )
362 .await?;
363 }
364
365 if analyze_types.contains(&"classes".to_string()) || analyze_types.contains(&"all".to_string())
367 {
368 unused_classes = find_unused_classes(
369 server,
370 confidence_threshold,
371 consider_external_apis,
372 &exclude_patterns,
373 )
374 .await?;
375 }
376
377 if analyze_types.contains(&"variables".to_string())
379 || analyze_types.contains(&"all".to_string())
380 {
381 unused_variables =
382 find_unused_variables(server, confidence_threshold, &exclude_patterns).await?;
383 }
384
385 if analyze_types.contains(&"imports".to_string()) || analyze_types.contains(&"all".to_string())
387 {
388 unused_imports =
389 find_unused_imports(server, confidence_threshold, &exclude_patterns).await?;
390 }
391
392 if include_dead_code {
394 dead_code_blocks =
395 find_dead_code_blocks(server, confidence_threshold, &exclude_patterns).await?;
396 }
397
398 let recommendations = get_unused_code_recommendations(
400 &unused_functions,
401 &unused_classes,
402 &unused_variables,
403 &unused_imports,
404 &dead_code_blocks,
405 );
406
407 let result = serde_json::json!({
408 "scope": scope,
409 "analysis_parameters": {
410 "include_dead_code": include_dead_code,
411 "consider_external_apis": consider_external_apis,
412 "confidence_threshold": confidence_threshold,
413 "analyze_types": analyze_types,
414 "exclude_patterns": exclude_patterns
415 },
416 "unused_code": {
417 "functions": unused_functions,
418 "classes": unused_classes,
419 "variables": unused_variables,
420 "imports": unused_imports,
421 "dead_code_blocks": dead_code_blocks
422 },
423 "summary": {
424 "total_unused_functions": unused_functions.len(),
425 "total_unused_classes": unused_classes.len(),
426 "total_unused_variables": unused_variables.len(),
427 "total_unused_imports": unused_imports.len(),
428 "total_dead_code_blocks": dead_code_blocks.len(),
429 "total_unused_elements": unused_functions.len() + unused_classes.len() + unused_variables.len() + unused_imports.len() + dead_code_blocks.len(),
430 "analysis_status": "completed"
431 },
432 "recommendations": recommendations,
433 "analysis_metadata": {
434 "version": "2.0.0",
435 "timestamp": chrono::Utc::now().to_rfc3339(),
436 "note": "Production-quality unused code analysis using graph-based detection"
437 }
438 });
439
440 Ok(CallToolResult {
441 content: vec![ToolContent::Text {
442 text: serde_json::to_string_pretty(&result)?,
443 }],
444 is_error: Some(false),
445 })
446}
447
448async fn analyze_security(
450 server: &CodePrismMcpServer,
451 arguments: Option<&Value>,
452) -> Result<CallToolResult> {
453 let default_args = serde_json::json!({});
454 let args = arguments.unwrap_or(&default_args);
455
456 let scope = args
457 .get("scope")
458 .and_then(|v| v.as_str())
459 .unwrap_or("repository");
460
461 let vulnerability_types = args
462 .get("vulnerability_types")
463 .and_then(|v| v.as_array())
464 .map(|arr| {
465 arr.iter()
466 .filter_map(|v| v.as_str())
467 .map(|s| s.to_string())
468 .collect::<Vec<_>>()
469 })
470 .unwrap_or_else(|| {
471 vec![
472 "injection".to_string(),
473 "xss".to_string(),
474 "csrf".to_string(),
475 "authentication".to_string(),
476 ]
477 });
478
479 let severity_threshold = args
480 .get("severity_threshold")
481 .and_then(|v| v.as_str())
482 .unwrap_or("medium");
483
484 let analyzer = SecurityAnalyzer::new();
486 let mut all_vulnerabilities = Vec::new();
487 let mut files_analyzed = 0;
488 let mut analysis_errors = Vec::new();
489
490 if let Some(repo_path) = server.repository_path() {
492 match server.scanner().discover_files(repo_path) {
493 Ok(files) => {
494 for file_path in files {
495 if should_analyze_file_for_security(&file_path) {
497 match std::fs::read_to_string(&file_path) {
498 Ok(content) => {
499 files_analyzed += 1;
500 match analyzer.analyze_content_with_location(
501 &content,
502 Some(&file_path.display().to_string()),
503 &vulnerability_types,
504 severity_threshold,
505 ) {
506 Ok(vulnerabilities) => {
507 all_vulnerabilities.extend(vulnerabilities);
508 }
509 Err(e) => {
510 analysis_errors.push(format!(
511 "Error analyzing {}: {}",
512 file_path.display(),
513 e
514 ));
515 }
516 }
517 }
518 Err(_) => {
519 continue;
521 }
522 }
523 }
524 }
525 }
526 Err(e) => {
527 return Ok(CallToolResult {
528 content: vec![ToolContent::Text {
529 text: format!("Failed to scan repository: {}", e),
530 }],
531 is_error: Some(true),
532 });
533 }
534 }
535 } else {
536 return Ok(CallToolResult {
537 content: vec![ToolContent::Text {
538 text: "No repository loaded for security analysis".to_string(),
539 }],
540 is_error: Some(true),
541 });
542 }
543
544 let security_report = analyzer.generate_security_report(&all_vulnerabilities);
546
547 let formatted_vulnerabilities: Vec<serde_json::Value> = all_vulnerabilities
549 .iter()
550 .map(|vuln| {
551 serde_json::json!({
552 "type": vuln.vulnerability_type,
553 "severity": vuln.severity,
554 "description": vuln.description,
555 "location": vuln.location,
556 "file_path": vuln.file_path,
557 "line_number": vuln.line_number,
558 "recommendation": vuln.recommendation,
559 "cvss_score": vuln.cvss_score,
560 "owasp_category": vuln.owasp_category,
561 "confidence": vuln.confidence
562 })
563 })
564 .collect();
565
566 let result = serde_json::json!({
567 "scope": scope,
568 "analysis_parameters": {
569 "vulnerability_types": vulnerability_types,
570 "severity_threshold": severity_threshold,
571 "files_analyzed": files_analyzed
572 },
573 "vulnerabilities": formatted_vulnerabilities,
574 "security_report": security_report,
575 "analysis_metadata": {
576 "total_files_scanned": files_analyzed,
577 "analysis_errors": analysis_errors.len(),
578 "errors": if analysis_errors.is_empty() { None } else { Some(analysis_errors) }
579 },
580 "summary": format!(
581 "Security analysis completed: {} vulnerabilities found across {} files",
582 all_vulnerabilities.len(),
583 files_analyzed
584 )
585 });
586
587 Ok(CallToolResult {
588 content: vec![ToolContent::Text {
589 text: serde_json::to_string_pretty(&result)?,
590 }],
591 is_error: Some(false),
592 })
593}
594
595fn should_analyze_file_for_security(file_path: &Path) -> bool {
597 if let Some(extension) = file_path.extension().and_then(|e| e.to_str()) {
598 let ext = extension.to_lowercase();
599 matches!(
600 ext.as_str(),
601 "js" | "jsx"
602 | "ts"
603 | "tsx"
604 | "py"
605 | "java"
606 | "php"
607 | "rb"
608 | "go"
609 | "rs"
610 | "c"
611 | "cpp"
612 | "cs"
613 | "html"
614 | "htm"
615 | "xml"
616 | "sql"
617 | "sh"
618 | "bash"
619 | "ps1"
620 | "yaml"
621 | "yml"
622 | "json"
623 | "properties"
624 | "ini"
625 | "conf"
626 | "config"
627 | "env"
628 | "dockerfile"
629 )
630 } else {
631 if let Some(filename) = file_path.file_name().and_then(|n| n.to_str()) {
633 matches!(
634 filename.to_lowercase().as_str(),
635 "dockerfile" | "makefile" | "jenkinsfile" | ".env"
636 )
637 } else {
638 false
639 }
640 }
641}
642
643async fn analyze_performance(
645 server: &CodePrismMcpServer,
646 arguments: Option<&Value>,
647) -> Result<CallToolResult> {
648 let default_args = serde_json::json!({});
649 let args = arguments.unwrap_or(&default_args);
650
651 let scope = args
652 .get("scope")
653 .and_then(|v| v.as_str())
654 .unwrap_or("repository");
655
656 let analysis_types = args
657 .get("analysis_types")
658 .and_then(|v| v.as_array())
659 .map(|arr| {
660 arr.iter()
661 .filter_map(|v| v.as_str().map(|s| s.to_string()))
662 .collect::<Vec<_>>()
663 })
664 .unwrap_or_else(|| {
665 vec![
666 "time_complexity".to_string(),
667 "memory_usage".to_string(),
668 "hot_spots".to_string(),
669 ]
670 });
671
672 let complexity_threshold = args
673 .get("complexity_threshold")
674 .and_then(|v| v.as_str())
675 .unwrap_or("medium");
676
677 let include_algorithmic_analysis = args
678 .get("include_algorithmic_analysis")
679 .and_then(|v| v.as_bool())
680 .unwrap_or(true);
681
682 let detect_bottlenecks = args
683 .get("detect_bottlenecks")
684 .and_then(|v| v.as_bool())
685 .unwrap_or(true);
686
687 let exclude_patterns = args
688 .get("exclude_patterns")
689 .and_then(|v| v.as_array())
690 .map(|arr| {
691 arr.iter()
692 .filter_map(|v| v.as_str().map(|s| s.to_string()))
693 .collect::<Vec<_>>()
694 })
695 .unwrap_or_default();
696
697 let mut all_issues = Vec::new();
699
700 if analysis_types.contains(&"time_complexity".to_string())
702 || analysis_types.contains(&"all".to_string())
703 {
704 let time_issues =
705 analyze_time_complexity(server, &exclude_patterns, include_algorithmic_analysis)
706 .await?;
707 all_issues.extend(time_issues);
708 }
709
710 if analysis_types.contains(&"memory_usage".to_string())
712 || analysis_types.contains(&"all".to_string())
713 {
714 let memory_issues = analyze_memory_usage(server, &exclude_patterns).await?;
715 all_issues.extend(memory_issues);
716 }
717
718 if analysis_types.contains(&"hot_spots".to_string())
720 || analysis_types.contains(&"all".to_string())
721 {
722 let hot_spot_issues =
723 detect_performance_hot_spots(server, &exclude_patterns, detect_bottlenecks).await?;
724 all_issues.extend(hot_spot_issues);
725 }
726
727 if analysis_types.contains(&"anti_patterns".to_string())
729 || analysis_types.contains(&"all".to_string())
730 {
731 let anti_pattern_issues =
732 detect_performance_anti_patterns(server, &exclude_patterns).await?;
733 all_issues.extend(anti_pattern_issues);
734 }
735
736 if analysis_types.contains(&"scalability".to_string())
738 || analysis_types.contains(&"all".to_string())
739 {
740 let scalability_issues = analyze_scalability_concerns(server, &exclude_patterns).await?;
741 all_issues.extend(scalability_issues);
742 }
743
744 let complexity_order = ["low", "medium", "high"];
746 let min_complexity_index = complexity_order
747 .iter()
748 .position(|&s| s == complexity_threshold)
749 .unwrap_or(1);
750
751 all_issues.retain(|issue| {
752 if let Some(complexity) = issue.get("complexity").and_then(|c| c.as_str()) {
753 complexity_order
754 .iter()
755 .position(|&s| s == complexity)
756 .unwrap_or(0)
757 >= min_complexity_index
758 } else {
759 true
760 }
761 });
762
763 let performance_score = calculate_performance_score(&all_issues);
765
766 let recommendations = get_performance_recommendations(&all_issues);
768
769 let mut by_category = std::collections::HashMap::new();
771 for issue in &all_issues {
772 if let Some(category) = issue.get("category").and_then(|c| c.as_str()) {
773 by_category
774 .entry(category.to_string())
775 .or_insert_with(Vec::new)
776 .push(issue);
777 }
778 }
779
780 let result = serde_json::json!({
781 "scope": scope,
782 "analysis_parameters": {
783 "analysis_types": analysis_types,
784 "complexity_threshold": complexity_threshold,
785 "include_algorithmic_analysis": include_algorithmic_analysis,
786 "detect_bottlenecks": detect_bottlenecks,
787 "exclude_patterns": exclude_patterns
788 },
789 "performance_issues": all_issues,
790 "performance_summary": {
791 "total_issues": all_issues.len(),
792 "performance_score": performance_score,
793 "issues_by_category": by_category.iter().map(|(k, v)| (k, v.len())).collect::<std::collections::HashMap<_, _>>(),
794 "critical_issues": all_issues.iter().filter(|i|
795 i.get("severity").and_then(|s| s.as_str()) == Some("critical")
796 ).count(),
797 "high_priority_issues": all_issues.iter().filter(|i|
798 i.get("severity").and_then(|s| s.as_str()) == Some("high")
799 ).count()
800 },
801 "recommendations": recommendations,
802 "analysis_metadata": {
803 "version": "2.0.0",
804 "timestamp": chrono::Utc::now().to_rfc3339(),
805 "note": "Production-quality performance analysis using static code analysis"
806 }
807 });
808
809 Ok(CallToolResult {
810 content: vec![ToolContent::Text {
811 text: serde_json::to_string_pretty(&result)?,
812 }],
813 is_error: Some(false),
814 })
815}
816
817async fn analyze_api_surface(
819 server: &CodePrismMcpServer,
820 arguments: Option<&Value>,
821) -> Result<CallToolResult> {
822 let default_args = serde_json::json!({});
823 let args = arguments.unwrap_or(&default_args);
824
825 let scope = args
826 .get("scope")
827 .and_then(|v| v.as_str())
828 .unwrap_or("repository");
829
830 let analysis_types = args
831 .get("analysis_types")
832 .and_then(|v| v.as_array())
833 .map(|arr| {
834 arr.iter()
835 .filter_map(|v| v.as_str().map(|s| s.to_string()))
836 .collect::<Vec<_>>()
837 })
838 .unwrap_or_else(|| {
839 vec![
840 "public_api".to_string(),
841 "versioning".to_string(),
842 "breaking_changes".to_string(),
843 ]
844 });
845
846 let include_private_apis = args
847 .get("include_private_apis")
848 .and_then(|v| v.as_bool())
849 .unwrap_or(false);
850
851 let api_version = args.get("api_version").and_then(|v| v.as_str());
852
853 let check_documentation_coverage = args
854 .get("check_documentation_coverage")
855 .and_then(|v| v.as_bool())
856 .unwrap_or(true);
857
858 let detect_breaking_changes = args
859 .get("detect_breaking_changes")
860 .and_then(|v| v.as_bool())
861 .unwrap_or(true);
862
863 let exclude_patterns = args
864 .get("exclude_patterns")
865 .and_then(|v| v.as_array())
866 .map(|arr| {
867 arr.iter()
868 .filter_map(|v| v.as_str().map(|s| s.to_string()))
869 .collect::<Vec<_>>()
870 })
871 .unwrap_or_default();
872
873 let mut all_issues = Vec::new();
875
876 if analysis_types.contains(&"public_api".to_string())
878 || analysis_types.contains(&"all".to_string())
879 {
880 let public_api_issues =
881 analyze_public_api(server, &exclude_patterns, include_private_apis).await?;
882 all_issues.extend(public_api_issues);
883 }
884
885 if analysis_types.contains(&"versioning".to_string())
887 || analysis_types.contains(&"all".to_string())
888 {
889 let versioning_issues =
890 analyze_api_versioning(server, &exclude_patterns, api_version).await?;
891 all_issues.extend(versioning_issues);
892 }
893
894 if (analysis_types.contains(&"all".to_string())
896 || analysis_types.contains(&"breaking_changes".to_string()))
897 && detect_breaking_changes
898 {
899 let breaking_change_issues = detect_api_breaking_changes(server, &exclude_patterns).await?;
900 all_issues.extend(breaking_change_issues);
901 }
902
903 if (analysis_types.contains(&"all".to_string())
905 || analysis_types.contains(&"documentation_coverage".to_string()))
906 && check_documentation_coverage
907 {
908 let doc_coverage_issues =
909 analyze_api_documentation_coverage(server, &exclude_patterns).await?;
910 all_issues.extend(doc_coverage_issues);
911 }
912
913 if analysis_types.contains(&"compatibility".to_string())
915 || analysis_types.contains(&"all".to_string())
916 {
917 let compatibility_issues =
918 analyze_api_compatibility(server, &exclude_patterns, api_version).await?;
919 all_issues.extend(compatibility_issues);
920 }
921
922 let api_health_score = calculate_api_health_score(&all_issues);
924
925 let recommendations = get_api_recommendations(&all_issues);
927
928 let mut api_elements = Vec::new();
930 let functions = server
931 .graph_store()
932 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
933 let classes = server
934 .graph_store()
935 .get_nodes_by_kind(codeprism_core::NodeKind::Class);
936
937 for function in functions {
938 if is_public_api_element(&function.name) {
939 api_elements.push(serde_json::json!({
940 "type": "function",
941 "name": function.name,
942 "file": function.file.display().to_string(),
943 "location": {
944 "start_line": function.span.start_line,
945 "end_line": function.span.end_line
946 },
947 "visibility": if function.name.starts_with('_') { "private" } else { "public" }
948 }));
949 }
950 }
951
952 for class in classes {
953 if is_public_api_element(&class.name) {
954 api_elements.push(serde_json::json!({
955 "type": "class",
956 "name": class.name,
957 "file": class.file.display().to_string(),
958 "location": {
959 "start_line": class.span.start_line,
960 "end_line": class.span.end_line
961 },
962 "visibility": if class.name.starts_with('_') { "private" } else { "public" }
963 }));
964 }
965 }
966
967 let mut by_category = std::collections::HashMap::new();
969 for issue in &all_issues {
970 if let Some(category) = issue.get("category").and_then(|c| c.as_str()) {
971 by_category
972 .entry(category.to_string())
973 .or_insert_with(Vec::new)
974 .push(issue);
975 }
976 }
977
978 let result = serde_json::json!({
979 "scope": scope,
980 "analysis_parameters": {
981 "analysis_types": analysis_types,
982 "include_private_apis": include_private_apis,
983 "api_version": api_version,
984 "check_documentation_coverage": check_documentation_coverage,
985 "detect_breaking_changes": detect_breaking_changes,
986 "exclude_patterns": exclude_patterns
987 },
988 "api_surface": {
989 "total_api_elements": api_elements.len(),
990 "public_functions": api_elements.iter().filter(|e|
991 e.get("type").and_then(|t| t.as_str()) == Some("function") &&
992 e.get("visibility").and_then(|v| v.as_str()) == Some("public")
993 ).count(),
994 "public_classes": api_elements.iter().filter(|e|
995 e.get("type").and_then(|t| t.as_str()) == Some("class") &&
996 e.get("visibility").and_then(|v| v.as_str()) == Some("public")
997 ).count(),
998 "api_elements": api_elements
999 },
1000 "api_issues": all_issues,
1001 "api_summary": {
1002 "total_issues": all_issues.len(),
1003 "api_health_score": api_health_score,
1004 "issues_by_category": by_category.iter().map(|(k, v)| (k, v.len())).collect::<std::collections::HashMap<_, _>>(),
1005 "critical_issues": all_issues.iter().filter(|i|
1006 i.get("severity").and_then(|s| s.as_str()) == Some("critical")
1007 ).count(),
1008 "breaking_changes": all_issues.iter().filter(|i|
1009 i.get("type").and_then(|t| t.as_str()).map(|s| s.contains("Breaking")) == Some(true)
1010 ).count()
1011 },
1012 "recommendations": recommendations,
1013 "analysis_metadata": {
1014 "version": "2.0.0",
1015 "timestamp": chrono::Utc::now().to_rfc3339(),
1016 "note": "Production-quality API surface analysis using comprehensive API detection"
1017 }
1018 });
1019
1020 Ok(CallToolResult {
1021 content: vec![ToolContent::Text {
1022 text: serde_json::to_string_pretty(&result)?,
1023 }],
1024 is_error: Some(false),
1025 })
1026}
1027
1028async fn find_unused_functions(
1030 server: &CodePrismMcpServer,
1031 confidence_threshold: f64,
1032 consider_external_apis: bool,
1033 exclude_patterns: &[String],
1034) -> Result<Vec<serde_json::Value>> {
1035 let mut unused_functions = Vec::new();
1036 let functions = server
1037 .graph_store()
1038 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
1039
1040 for function in functions {
1041 if exclude_patterns
1043 .iter()
1044 .any(|pattern| function.file.to_string_lossy().contains(pattern))
1045 {
1046 continue;
1047 }
1048
1049 let references = server.graph_query().find_references(&function.id)?;
1050 let mut confidence = 1.0;
1051 let mut usage_indicators = Vec::new();
1052
1053 let call_count = references
1055 .iter()
1056 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Calls))
1057 .count();
1058
1059 if call_count == 0 {
1060 usage_indicators.push("No direct function calls found".to_string());
1061 } else {
1062 confidence -= (call_count as f64 * 0.3).min(0.8);
1063 usage_indicators.push(format!("{} function calls found", call_count));
1064 }
1065
1066 if consider_external_apis {
1068 let function_name = &function.name;
1069
1070 if function_name.starts_with("main")
1072 || function_name.starts_with("__")
1073 || function_name.contains("handler")
1074 || function_name.contains("callback")
1075 || function_name.contains("api")
1076 || function_name.contains("endpoint")
1077 {
1078 confidence -= 0.5;
1079 usage_indicators.push("Potentially used by external API".to_string());
1080 }
1081 }
1082
1083 if function.name.starts_with('_') {
1085 confidence += 0.1;
1087 usage_indicators.push("Private function (name starts with _)".to_string());
1088 }
1089
1090 if confidence >= confidence_threshold {
1091 unused_functions.push(serde_json::json!({
1092 "id": function.id.to_hex(),
1093 "name": function.name,
1094 "kind": "Function",
1095 "file": function.file.display().to_string(),
1096 "location": {
1097 "start_line": function.span.start_line,
1098 "end_line": function.span.end_line,
1099 "start_column": function.span.start_column,
1100 "end_column": function.span.end_column
1101 },
1102 "confidence": confidence,
1103 "usage_indicators": usage_indicators,
1104 "lines_of_code": function.span.end_line - function.span.start_line + 1,
1105 "potential_savings": "Remove function to reduce codebase size"
1106 }));
1107 }
1108 }
1109
1110 Ok(unused_functions)
1111}
1112
1113async fn find_unused_classes(
1115 server: &CodePrismMcpServer,
1116 confidence_threshold: f64,
1117 consider_external_apis: bool,
1118 exclude_patterns: &[String],
1119) -> Result<Vec<serde_json::Value>> {
1120 let mut unused_classes = Vec::new();
1121 let classes = server
1122 .graph_store()
1123 .get_nodes_by_kind(codeprism_core::NodeKind::Class);
1124
1125 for class in classes {
1126 if exclude_patterns
1128 .iter()
1129 .any(|pattern| class.file.to_string_lossy().contains(pattern))
1130 {
1131 continue;
1132 }
1133
1134 let references = server.graph_query().find_references(&class.id)?;
1135 let mut confidence = 1.0;
1136 let mut usage_indicators = Vec::new();
1137
1138 let usage_count = references
1140 .iter()
1141 .filter(|r| {
1142 matches!(
1143 r.edge_kind,
1144 codeprism_core::EdgeKind::Calls
1145 | codeprism_core::EdgeKind::Extends
1146 | codeprism_core::EdgeKind::Implements
1147 )
1148 })
1149 .count();
1150
1151 if usage_count == 0 {
1152 usage_indicators
1153 .push("No instantiation, inheritance, or implementation found".to_string());
1154 } else {
1155 confidence -= (usage_count as f64 * 0.4).min(0.9);
1156 usage_indicators.push(format!(
1157 "{} usages found (instantiation/inheritance)",
1158 usage_count
1159 ));
1160 }
1161
1162 if consider_external_apis {
1164 let class_name = &class.name;
1165
1166 if class_name.contains("Controller")
1167 || class_name.contains("Service")
1168 || class_name.contains("Handler")
1169 || class_name.contains("Model")
1170 || class_name.contains("Entity")
1171 || class_name.contains("Exception")
1172 || class_name.contains("Error")
1173 {
1174 confidence -= 0.4;
1175 usage_indicators
1176 .push("Potentially used by framework or external system".to_string());
1177 }
1178 }
1179
1180 if confidence >= confidence_threshold {
1181 unused_classes.push(serde_json::json!({
1182 "id": class.id.to_hex(),
1183 "name": class.name,
1184 "kind": "Class",
1185 "file": class.file.display().to_string(),
1186 "location": {
1187 "start_line": class.span.start_line,
1188 "end_line": class.span.end_line,
1189 "start_column": class.span.start_column,
1190 "end_column": class.span.end_column
1191 },
1192 "confidence": confidence,
1193 "usage_indicators": usage_indicators,
1194 "lines_of_code": class.span.end_line - class.span.start_line + 1,
1195 "potential_savings": "Remove class and its methods to reduce codebase complexity"
1196 }));
1197 }
1198 }
1199
1200 Ok(unused_classes)
1201}
1202
1203async fn find_unused_variables(
1205 server: &CodePrismMcpServer,
1206 confidence_threshold: f64,
1207 exclude_patterns: &[String],
1208) -> Result<Vec<serde_json::Value>> {
1209 let mut unused_variables = Vec::new();
1210 let variables = server
1211 .graph_store()
1212 .get_nodes_by_kind(codeprism_core::NodeKind::Variable);
1213
1214 for variable in variables {
1215 if exclude_patterns
1217 .iter()
1218 .any(|pattern| variable.file.to_string_lossy().contains(pattern))
1219 {
1220 continue;
1221 }
1222
1223 let references = server.graph_query().find_references(&variable.id)?;
1224 let mut confidence = 1.0;
1225 let mut usage_indicators = Vec::new();
1226
1227 let read_count = references
1229 .iter()
1230 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Reads))
1231 .count();
1232
1233 let write_count = references
1234 .iter()
1235 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Writes))
1236 .count();
1237
1238 if read_count == 0 && write_count <= 1 {
1239 usage_indicators.push("Variable assigned but never read".to_string());
1241 } else if read_count > 0 {
1242 confidence -= (read_count as f64 * 0.4).min(0.9);
1243 usage_indicators.push(format!("{} read operations found", read_count));
1244 }
1245
1246 let variable_name = &variable.name;
1248 if variable_name.starts_with('_') {
1249 confidence += 0.1;
1250 usage_indicators.push("Private variable (name starts with _)".to_string());
1251 }
1252
1253 if confidence >= confidence_threshold {
1254 unused_variables.push(serde_json::json!({
1255 "id": variable.id.to_hex(),
1256 "name": variable.name,
1257 "kind": "Variable",
1258 "file": variable.file.display().to_string(),
1259 "location": {
1260 "start_line": variable.span.start_line,
1261 "end_line": variable.span.end_line,
1262 "start_column": variable.span.start_column,
1263 "end_column": variable.span.end_column
1264 },
1265 "confidence": confidence,
1266 "usage_indicators": usage_indicators,
1267 "potential_savings": "Remove unused variable declaration"
1268 }));
1269 }
1270 }
1271
1272 Ok(unused_variables)
1273}
1274
1275async fn find_unused_imports(
1277 server: &CodePrismMcpServer,
1278 confidence_threshold: f64,
1279 exclude_patterns: &[String],
1280) -> Result<Vec<serde_json::Value>> {
1281 let mut unused_imports = Vec::new();
1282 let imports = server
1283 .graph_store()
1284 .get_nodes_by_kind(codeprism_core::NodeKind::Import);
1285
1286 for import in imports {
1287 if exclude_patterns
1289 .iter()
1290 .any(|pattern| import.file.to_string_lossy().contains(pattern))
1291 {
1292 continue;
1293 }
1294
1295 let references = server.graph_query().find_references(&import.id)?;
1296 let mut confidence = 1.0;
1297 let mut usage_indicators = Vec::new();
1298
1299 let usage_count = references
1301 .iter()
1302 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Imports))
1303 .count();
1304
1305 if usage_count == 0 {
1306 usage_indicators.push("Import statement not used in code".to_string());
1307 } else {
1308 confidence -= (usage_count as f64 * 0.5).min(0.9);
1309 usage_indicators.push(format!("{} usages of imported symbols found", usage_count));
1310 }
1311
1312 if confidence >= confidence_threshold {
1313 unused_imports.push(serde_json::json!({
1314 "id": import.id.to_hex(),
1315 "name": import.name,
1316 "kind": "Import",
1317 "file": import.file.display().to_string(),
1318 "location": {
1319 "start_line": import.span.start_line,
1320 "end_line": import.span.end_line,
1321 "start_column": import.span.start_column,
1322 "end_column": import.span.end_column
1323 },
1324 "confidence": confidence,
1325 "usage_indicators": usage_indicators,
1326 "potential_savings": "Remove unused import to clean dependencies"
1327 }));
1328 }
1329 }
1330
1331 Ok(unused_imports)
1332}
1333
1334async fn find_dead_code_blocks(
1336 server: &CodePrismMcpServer,
1337 confidence_threshold: f64,
1338 exclude_patterns: &[String],
1339) -> Result<Vec<serde_json::Value>> {
1340 let mut dead_code_blocks = Vec::new();
1341 let functions = server
1342 .graph_store()
1343 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
1344
1345 for function in functions {
1346 if exclude_patterns
1348 .iter()
1349 .any(|pattern| function.file.to_string_lossy().contains(pattern))
1350 {
1351 continue;
1352 }
1353
1354 let mut analysis_result = DeadCodeAnalysis::new(function.clone());
1355
1356 analyze_function_usage(server, &mut analysis_result).await?;
1358 analyze_reflection_patterns(server, &mut analysis_result).await?;
1359 analyze_framework_patterns(server, &mut analysis_result).await?;
1360 analyze_dynamic_dispatch(server, &mut analysis_result).await?;
1361 analyze_api_boundaries(server, &mut analysis_result).await?;
1362 analyze_unreachable_code(server, &mut analysis_result).await?;
1363
1364 if analysis_result.confidence >= confidence_threshold {
1365 let impact_analysis = perform_impact_analysis(server, &analysis_result).await?;
1366 let removal_suggestions =
1367 generate_removal_suggestions(&analysis_result, &impact_analysis);
1368
1369 dead_code_blocks.push(serde_json::json!({
1370 "id": function.id.to_hex(),
1371 "name": function.name,
1372 "kind": analysis_result.dead_code_type,
1373 "file": function.file.display().to_string(),
1374 "location": {
1375 "start_line": function.span.start_line,
1376 "end_line": function.span.end_line,
1377 "start_column": function.span.start_column,
1378 "end_column": function.span.end_column
1379 },
1380 "confidence": analysis_result.confidence,
1381 "dead_code_category": analysis_result.category,
1382 "indicators": analysis_result.indicators,
1383 "lines_of_code": function.span.end_line - function.span.start_line + 1,
1384 "potential_savings": format!("Remove {} to eliminate {} code", analysis_result.dead_code_type.to_lowercase(), analysis_result.category),
1385 "framework_context": analysis_result.framework_context,
1386 "cross_language_usage": analysis_result.cross_language_usage,
1387 "dynamic_usage_patterns": analysis_result.dynamic_patterns,
1388 "impact_analysis": impact_analysis,
1389 "removal_suggestions": removal_suggestions,
1390 "safety_score": analysis_result.safety_score
1391 }));
1392 }
1393 }
1394
1395 Ok(dead_code_blocks)
1396}
1397
1398#[derive(Debug, Clone)]
1400struct DeadCodeAnalysis {
1401 function: codeprism_core::Node,
1402 confidence: f64,
1403 dead_code_type: String,
1404 category: String,
1405 indicators: Vec<String>,
1406 framework_context: Vec<String>,
1407 cross_language_usage: Vec<String>,
1408 dynamic_patterns: Vec<String>,
1409 safety_score: f64,
1410 risk_factors: Vec<String>,
1411}
1412
1413impl DeadCodeAnalysis {
1414 fn new(function: codeprism_core::Node) -> Self {
1415 Self {
1416 function,
1417 confidence: 0.0,
1418 dead_code_type: "Unknown".to_string(),
1419 category: "Potentially Dead".to_string(),
1420 indicators: Vec::new(),
1421 framework_context: Vec::new(),
1422 cross_language_usage: Vec::new(),
1423 dynamic_patterns: Vec::new(),
1424 safety_score: 1.0,
1425 risk_factors: Vec::new(),
1426 }
1427 }
1428}
1429
1430async fn analyze_function_usage(
1432 server: &CodePrismMcpServer,
1433 analysis: &mut DeadCodeAnalysis,
1434) -> Result<()> {
1435 let function_name = &analysis.function.name;
1436
1437 let references = server
1439 .graph_query()
1440 .find_references(&analysis.function.id)?;
1441 let call_count = references
1442 .iter()
1443 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Calls))
1444 .count();
1445
1446 if call_count == 0 {
1447 analysis.confidence += 0.4;
1448 analysis
1449 .indicators
1450 .push("No direct function calls found".to_string());
1451
1452 if function_name.starts_with("main")
1454 || function_name.starts_with("__")
1455 || function_name == "init"
1456 || function_name.contains("entry")
1457 || function_name.contains("bootstrap")
1458 {
1459 analysis.confidence -= 0.6;
1460 analysis
1461 .indicators
1462 .push("Identified as potential entry point".to_string());
1463 analysis.safety_score -= 0.3;
1464 }
1465 } else {
1466 analysis
1467 .indicators
1468 .push(format!("{} direct calls found", call_count));
1469 }
1470
1471 let function_name_lower = function_name.to_lowercase();
1473
1474 if function_name_lower.contains("deprecated")
1476 || function_name_lower.contains("unused")
1477 || function_name_lower.contains("old")
1478 || function_name_lower.contains("legacy")
1479 || function_name_lower.contains("obsolete")
1480 {
1481 analysis.confidence += 0.7;
1482 analysis.dead_code_type = "Legacy Function".to_string();
1483 analysis.category = "Deprecated Code".to_string();
1484 analysis
1485 .indicators
1486 .push("Function name indicates deprecated/legacy code".to_string());
1487 }
1488
1489 if function_name_lower.contains("temp")
1491 || function_name_lower.contains("tmp")
1492 || function_name_lower.contains("debug")
1493 || function_name_lower.contains("test_")
1494 || function_name_lower.contains("_test")
1495 || function_name_lower.contains("draft")
1496 {
1497 analysis.confidence += 0.6;
1498 analysis.dead_code_type = "Temporary Function".to_string();
1499 analysis.category = "Debug/Test Code".to_string();
1500 analysis
1501 .indicators
1502 .push("Function name suggests temporary or debug code".to_string());
1503 }
1504
1505 if function_name_lower.contains("_v1")
1507 || function_name_lower.contains("_old")
1508 || function_name_lower.contains("_backup")
1509 || function_name_lower.contains("_copy")
1510 {
1511 analysis.confidence += 0.5;
1512 analysis.dead_code_type = "Versioned Function".to_string();
1513 analysis.category = "Superseded Code".to_string();
1514 analysis
1515 .indicators
1516 .push("Function appears to be an old version".to_string());
1517 }
1518
1519 Ok(())
1520}
1521
1522async fn analyze_reflection_patterns(
1524 server: &CodePrismMcpServer,
1525 analysis: &mut DeadCodeAnalysis,
1526) -> Result<()> {
1527 let function_name = &analysis.function.name;
1528
1529 let reflection_patterns = vec![
1531 ("Class.forName", "Java reflection"),
1533 ("Method.invoke", "Java method invocation"),
1534 ("Constructor.newInstance", "Java constructor reflection"),
1535 ("getattr", "Python dynamic attribute access"),
1537 ("__getattribute__", "Python attribute access"),
1538 ("exec", "Python dynamic execution"),
1539 ("eval", "Python expression evaluation"),
1540 ("Function.prototype.call", "JavaScript dynamic call"),
1542 ("Function.prototype.apply", "JavaScript dynamic apply"),
1543 ("eval", "JavaScript dynamic evaluation"),
1544 ("Type.GetMethod", "C# reflection"),
1546 ("MethodInfo.Invoke", "C# method invocation"),
1547 ("dynamic", "Dynamic typing/loading"),
1549 ("runtime", "Runtime loading"),
1550 ];
1551
1552 let all_functions = server
1554 .graph_store()
1555 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
1556
1557 for other_function in all_functions {
1558 let other_name_lower = other_function.name.to_lowercase();
1559
1560 for (pattern, description) in &reflection_patterns {
1561 if other_name_lower.contains(&pattern.to_lowercase()) {
1562 if other_function.name.contains(function_name) {
1564 analysis.confidence -= 0.4;
1565 analysis
1566 .dynamic_patterns
1567 .push(format!("{}: {}", description, other_function.name));
1568 analysis
1569 .indicators
1570 .push(format!("Potential dynamic usage via {}", description));
1571 analysis.safety_score -= 0.2;
1572 }
1573 }
1574 }
1575 }
1576
1577 if function_name.len() > 3 {
1579 analysis
1581 .dynamic_patterns
1582 .push("String literal analysis placeholder".to_string());
1583 }
1584
1585 Ok(())
1586}
1587
1588async fn analyze_framework_patterns(
1590 _server: &CodePrismMcpServer,
1591 analysis: &mut DeadCodeAnalysis,
1592) -> Result<()> {
1593 let function_name = &analysis.function.name;
1594 let function_name_lower = function_name.to_lowercase();
1595 let file_path = analysis.function.file.to_string_lossy();
1596
1597 if function_name_lower.contains("handler")
1599 || function_name_lower.contains("controller")
1600 || function_name_lower.contains("endpoint")
1601 || function_name_lower.contains("route")
1602 || function_name_lower.starts_with("get_")
1603 || function_name_lower.starts_with("post_")
1604 || function_name_lower.starts_with("put_")
1605 || function_name_lower.starts_with("delete_")
1606 {
1607 analysis.confidence -= 0.5;
1608 analysis
1609 .framework_context
1610 .push("Web framework handler".to_string());
1611 analysis
1612 .indicators
1613 .push("Identified as web framework handler/controller".to_string());
1614 analysis.safety_score -= 0.4;
1615 }
1616
1617 if function_name_lower.starts_with("test_")
1619 || function_name_lower.contains("_test")
1620 || function_name_lower.starts_with("spec_")
1621 || function_name_lower.contains("should_")
1622 || file_path.contains("test")
1623 {
1624 if file_path.contains("test") {
1625 analysis.confidence -= 0.3;
1627 analysis
1628 .framework_context
1629 .push("Test framework function".to_string());
1630 } else {
1631 analysis.confidence += 0.2;
1633 analysis
1634 .framework_context
1635 .push("Test function in non-test file".to_string());
1636 }
1637 }
1638
1639 if function_name_lower.contains("migration")
1641 || function_name_lower.contains("seed")
1642 || function_name_lower.contains("fixture")
1643 || function_name_lower.starts_with("up_")
1644 || function_name_lower.starts_with("down_")
1645 {
1646 analysis.confidence -= 0.4;
1647 analysis
1648 .framework_context
1649 .push("Database migration/ORM function".to_string());
1650 analysis.safety_score -= 0.3;
1651 }
1652
1653 if function_name_lower.starts_with("on_")
1655 || function_name_lower.contains("callback")
1656 || function_name_lower.contains("listener")
1657 || function_name_lower.contains("handler")
1658 || function_name_lower.ends_with("_event")
1659 {
1660 analysis.confidence -= 0.4;
1661 analysis
1662 .framework_context
1663 .push("Event callback function".to_string());
1664 analysis.safety_score -= 0.3;
1665 }
1666
1667 if function_name_lower.starts_with("cmd_")
1669 || function_name_lower.contains("command")
1670 || function_name_lower.contains("cli_")
1671 || function_name_lower.starts_with("do_")
1672 {
1673 analysis.confidence -= 0.3;
1674 analysis
1675 .framework_context
1676 .push("CLI command function".to_string());
1677 analysis.safety_score -= 0.2;
1678 }
1679
1680 if function_name_lower.contains("plugin")
1682 || function_name_lower.contains("extension")
1683 || function_name_lower.contains("hook")
1684 || function_name_lower.contains("filter")
1685 {
1686 analysis.confidence -= 0.5;
1687 analysis
1688 .framework_context
1689 .push("Plugin/extension function".to_string());
1690 analysis.safety_score -= 0.4;
1691 }
1692
1693 Ok(())
1694}
1695
1696async fn analyze_dynamic_dispatch(
1698 server: &CodePrismMcpServer,
1699 analysis: &mut DeadCodeAnalysis,
1700) -> Result<()> {
1701 let function_name = &analysis.function.name;
1702
1703 let classes = server
1705 .graph_store()
1706 .get_nodes_by_kind(codeprism_core::NodeKind::Class);
1707
1708 for class in classes {
1709 let class_dependencies = server
1710 .graph_query()
1711 .find_dependencies(&class.id, codeprism_core::graph::DependencyType::Direct)?;
1712
1713 for dep in class_dependencies {
1714 if matches!(dep.edge_kind, codeprism_core::EdgeKind::Implements)
1716 && dep.target_node.name == *function_name
1717 {
1718 analysis.confidence -= 0.5;
1719 analysis
1720 .dynamic_patterns
1721 .push(format!("Implements interface: {}", class.name));
1722 analysis
1723 .indicators
1724 .push("Function implements interface (potential dynamic dispatch)".to_string());
1725 analysis.safety_score -= 0.3;
1726 break;
1727 }
1728 }
1729 }
1730
1731 let function_name_lower = function_name.to_lowercase();
1733 if function_name_lower.contains("virtual")
1734 || function_name_lower.contains("override")
1735 || function_name_lower.contains("abstract")
1736 {
1737 analysis.confidence -= 0.4;
1738 analysis
1739 .dynamic_patterns
1740 .push("Virtual/override method".to_string());
1741 analysis.safety_score -= 0.3;
1742 }
1743
1744 Ok(())
1745}
1746
1747async fn analyze_api_boundaries(
1749 _server: &CodePrismMcpServer,
1750 analysis: &mut DeadCodeAnalysis,
1751) -> Result<()> {
1752 let function_name = &analysis.function.name;
1753 let function_name_lower = function_name.to_lowercase();
1754
1755 if function_name_lower.starts_with("extern")
1757 || function_name_lower.contains("export")
1758 || function_name_lower.contains("api_")
1759 || function_name_lower.starts_with("c_")
1760 {
1761 analysis.confidence -= 0.6;
1762 analysis
1763 .cross_language_usage
1764 .push("C API export".to_string());
1765 analysis.safety_score -= 0.4;
1766 }
1767
1768 if function_name_lower.starts_with("java_")
1770 || function_name_lower.contains("jni_")
1771 || function_name_lower.contains("_jni")
1772 {
1773 analysis.confidence -= 0.6;
1774 analysis
1775 .cross_language_usage
1776 .push("JNI function".to_string());
1777 analysis.safety_score -= 0.4;
1778 }
1779
1780 if function_name_lower.starts_with("py_")
1782 || function_name_lower.contains("python_")
1783 || function_name_lower.contains("_py")
1784 {
1785 analysis.confidence -= 0.6;
1786 analysis
1787 .cross_language_usage
1788 .push("Python C extension".to_string());
1789 analysis.safety_score -= 0.4;
1790 }
1791
1792 if function_name_lower.contains("ffi")
1794 || function_name_lower.contains("foreign")
1795 || function_name_lower.contains("native")
1796 {
1797 analysis.confidence -= 0.5;
1798 analysis
1799 .cross_language_usage
1800 .push("Foreign function interface".to_string());
1801 analysis.safety_score -= 0.3;
1802 }
1803
1804 let file_path = analysis.function.file.to_string_lossy();
1806 if file_path.ends_with(".h") || file_path.ends_with(".hpp") {
1807 analysis.confidence -= 0.3;
1809 analysis
1810 .cross_language_usage
1811 .push("Header file declaration".to_string());
1812 analysis.safety_score -= 0.2;
1813 }
1814
1815 Ok(())
1816}
1817
1818async fn analyze_unreachable_code(
1820 server: &CodePrismMcpServer,
1821 analysis: &mut DeadCodeAnalysis,
1822) -> Result<()> {
1823 let function = &analysis.function;
1824
1825 let file_path = function.file.to_string_lossy();
1827
1828 let module_functions = server
1830 .graph_store()
1831 .get_nodes_by_kind(codeprism_core::NodeKind::Function)
1832 .into_iter()
1833 .filter(|f| f.file == function.file)
1834 .collect::<Vec<_>>();
1835
1836 let mut unused_count = 0;
1837 for module_function in &module_functions {
1838 let references = server.graph_query().find_references(&module_function.id)?;
1839 let call_count = references
1840 .iter()
1841 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Calls))
1842 .count();
1843 if call_count == 0 {
1844 unused_count += 1;
1845 }
1846 }
1847
1848 if module_functions.len() > 1 && unused_count as f64 / module_functions.len() as f64 > 0.8 {
1849 analysis.confidence += 0.3;
1850 analysis
1851 .indicators
1852 .push("Function is in a mostly unused module".to_string());
1853 analysis.category = "Unreachable Module".to_string();
1854 }
1855
1856 if file_path.contains("debug") && !file_path.contains("test") {
1858 analysis
1859 .indicators
1860 .push("Function in debug-specific code".to_string());
1861 analysis.confidence += 0.2;
1862 }
1863
1864 if file_path.contains("experimental") || file_path.contains("prototype") {
1865 analysis
1866 .indicators
1867 .push("Function in experimental/prototype code".to_string());
1868 analysis.confidence += 0.4;
1869 analysis.category = "Experimental Code".to_string();
1870 }
1871
1872 Ok(())
1873}
1874
1875async fn perform_impact_analysis(
1877 server: &CodePrismMcpServer,
1878 analysis: &DeadCodeAnalysis,
1879) -> Result<serde_json::Value> {
1880 let function = &analysis.function;
1881
1882 let dependencies = server
1884 .graph_query()
1885 .find_dependencies(&function.id, codeprism_core::graph::DependencyType::Direct)?;
1886
1887 let mut impacted_files = std::collections::HashSet::new();
1888 let mut impacted_modules = std::collections::HashSet::new();
1889
1890 for dep in &dependencies {
1891 impacted_files.insert(dep.target_node.file.to_string_lossy().to_string());
1892 if let Some(parent) = dep.target_node.file.parent() {
1893 impacted_modules.insert(parent.to_string_lossy().to_string());
1894 }
1895 }
1896
1897 let complexity_score = match dependencies.len() {
1899 0 => "Low",
1900 1..=5 => "Medium",
1901 _ => "High",
1902 };
1903
1904 let lines_of_code = function.span.end_line - function.span.start_line + 1;
1906 let effort_estimate = match (lines_of_code, dependencies.len()) {
1907 (0..=10, 0..=2) => "5 minutes",
1908 (0..=50, 0..=5) => "15 minutes",
1909 (0..=100, 0..=10) => "30 minutes",
1910 _ => "1+ hours",
1911 };
1912
1913 Ok(serde_json::json!({
1914 "impact_scope": {
1915 "affected_files": impacted_files.len(),
1916 "affected_modules": impacted_modules.len(),
1917 "total_dependencies": dependencies.len()
1918 },
1919 "removal_complexity": complexity_score,
1920 "estimated_effort": effort_estimate,
1921 "risk_level": if analysis.safety_score > 0.7 { "Low" } else if analysis.safety_score > 0.4 { "Medium" } else { "High" },
1922 "safety_considerations": analysis.risk_factors,
1923 "testing_recommendations": generate_testing_recommendations(analysis)
1924 }))
1925}
1926
1927fn generate_removal_suggestions(
1929 analysis: &DeadCodeAnalysis,
1930 impact_analysis: &serde_json::Value,
1931) -> Vec<String> {
1932 let mut suggestions = Vec::new();
1933
1934 if analysis.safety_score > 0.8 {
1936 suggestions.push("✅ Safe to remove - low risk of unintended consequences".to_string());
1937 suggestions.push("Consider automated removal as part of code cleanup".to_string());
1938 } else if analysis.safety_score > 0.5 {
1939 suggestions.push("⚠️ Moderate risk - manual review recommended".to_string());
1940 suggestions.push("Remove after verifying no dynamic references exist".to_string());
1941 } else {
1942 suggestions.push("🚨 High risk - thorough analysis required".to_string());
1943 suggestions.push("Consider deprecation before removal".to_string());
1944 }
1945
1946 if !analysis.framework_context.is_empty() {
1948 suggestions.push(format!(
1949 "Framework context detected: {:?}",
1950 analysis.framework_context
1951 ));
1952 suggestions.push("Verify framework conventions before removal".to_string());
1953 }
1954
1955 if !analysis.cross_language_usage.is_empty() {
1957 suggestions.push("Cross-language usage detected - check all language bindings".to_string());
1958 }
1959
1960 let complexity = impact_analysis
1962 .get("removal_complexity")
1963 .and_then(|v| v.as_str())
1964 .unwrap_or("Unknown");
1965 match complexity {
1966 "Low" => suggestions.push("Can be included in automated batch removal".to_string()),
1967 "Medium" => suggestions.push("Group with similar functions for batch review".to_string()),
1968 "High" => suggestions.push("Handle individually with careful planning".to_string()),
1969 _ => {}
1970 }
1971
1972 suggestions
1973}
1974
1975fn generate_testing_recommendations(analysis: &DeadCodeAnalysis) -> Vec<String> {
1977 let mut recommendations = Vec::new();
1978
1979 if !analysis.framework_context.is_empty() {
1980 recommendations.push("Run framework-specific test suite".to_string());
1981 }
1982
1983 if !analysis.cross_language_usage.is_empty() {
1984 recommendations.push("Test cross-language bindings and integrations".to_string());
1985 }
1986
1987 if analysis.safety_score < 0.5 {
1988 recommendations.push("Comprehensive integration testing required".to_string());
1989 recommendations.push("Monitor production metrics after removal".to_string());
1990 } else {
1991 recommendations.push("Standard unit test coverage sufficient".to_string());
1992 }
1993
1994 recommendations
1995}
1996
1997fn get_unused_code_recommendations(
1999 unused_functions: &[serde_json::Value],
2000 unused_classes: &[serde_json::Value],
2001 unused_variables: &[serde_json::Value],
2002 unused_imports: &[serde_json::Value],
2003 dead_code_blocks: &[serde_json::Value],
2004) -> Vec<String> {
2005 let mut recommendations = Vec::new();
2006
2007 if !unused_imports.is_empty() {
2008 recommendations.push(format!(
2009 "Remove {} unused imports to clean up dependencies",
2010 unused_imports.len()
2011 ));
2012 }
2013
2014 if !unused_variables.is_empty() {
2015 recommendations.push(format!(
2016 "Remove {} unused variables to reduce code clutter",
2017 unused_variables.len()
2018 ));
2019 }
2020
2021 if !unused_functions.is_empty() {
2022 let lines_saved: usize = unused_functions
2023 .iter()
2024 .filter_map(|f| f.get("lines_of_code").and_then(|v| v.as_u64()))
2025 .map(|v| v as usize)
2026 .sum();
2027 recommendations.push(format!(
2028 "Remove {} unused functions to save approximately {} lines of code",
2029 unused_functions.len(),
2030 lines_saved
2031 ));
2032 }
2033
2034 if !unused_classes.is_empty() {
2035 let lines_saved: usize = unused_classes
2036 .iter()
2037 .filter_map(|c| c.get("lines_of_code").and_then(|v| v.as_u64()))
2038 .map(|v| v as usize)
2039 .sum();
2040 recommendations.push(format!(
2041 "Remove {} unused classes to save approximately {} lines of code",
2042 unused_classes.len(),
2043 lines_saved
2044 ));
2045 }
2046
2047 if !dead_code_blocks.is_empty() {
2048 recommendations.push(format!(
2049 "Remove {} dead code blocks to eliminate unreachable code",
2050 dead_code_blocks.len()
2051 ));
2052 }
2053
2054 if recommendations.is_empty() {
2055 recommendations
2056 .push("No unused code detected with current confidence threshold".to_string());
2057 } else {
2058 recommendations.push("Consider running tests after removing unused code to ensure no unexpected dependencies".to_string());
2059 recommendations
2060 .push("Use version control to safely experiment with unused code removal".to_string());
2061 }
2062
2063 recommendations
2064}
2065
2066async fn analyze_time_complexity(
2068 server: &CodePrismMcpServer,
2069 exclude_patterns: &[String],
2070 include_algorithmic_analysis: bool,
2071) -> Result<Vec<serde_json::Value>> {
2072 let mut issues = Vec::new();
2073 let functions = server
2074 .graph_store()
2075 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2076
2077 for function in functions {
2078 if exclude_patterns
2079 .iter()
2080 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2081 {
2082 continue;
2083 }
2084
2085 let function_name_lower = function.name.to_lowercase();
2086
2087 if function_name_lower.contains("sort")
2089 || function_name_lower.contains("search")
2090 || function_name_lower.contains("find")
2091 || function_name_lower.contains("filter")
2092 {
2093 let mut complexity = "medium";
2094 let mut estimated_complexity = "O(n log n)";
2095
2096 if function_name_lower.contains("bubble")
2097 || function_name_lower.contains("selection")
2098 || function_name_lower.contains("insertion")
2099 {
2100 complexity = "high";
2101 estimated_complexity = "O(n^2)";
2102 } else if function_name_lower.contains("quick")
2103 || function_name_lower.contains("merge")
2104 || function_name_lower.contains("heap")
2105 {
2106 complexity = "medium";
2107 estimated_complexity = "O(n log n)";
2108 }
2109
2110 issues.push(serde_json::json!({
2111 "type": "Algorithmic Complexity",
2112 "category": "time_complexity",
2113 "severity": if complexity == "high" { "high" } else { "medium" },
2114 "complexity": complexity,
2115 "function": {
2116 "id": function.id.to_hex(),
2117 "name": function.name,
2118 "file": function.file.display().to_string(),
2119 "location": {
2120 "start_line": function.span.start_line,
2121 "end_line": function.span.end_line
2122 }
2123 },
2124 "description": format!("Function '{}' may have high algorithmic complexity", function.name),
2125 "estimated_complexity": estimated_complexity,
2126 "recommendation": "Consider using more efficient algorithms or data structures",
2127 "impact": "May cause performance issues with large datasets"
2128 }));
2129 }
2130
2131 if include_algorithmic_analysis {
2132 let function_lines = function.span.end_line - function.span.start_line + 1;
2133
2134 if function_lines > 50 {
2136 let dependencies = server.graph_query().find_dependencies(
2137 &function.id,
2138 codeprism_core::graph::DependencyType::Calls,
2139 )?;
2140
2141 if dependencies.len() > 20 {
2142 issues.push(serde_json::json!({
2143 "type": "Complex Algorithm",
2144 "category": "time_complexity",
2145 "severity": "medium",
2146 "complexity": "medium",
2147 "function": {
2148 "id": function.id.to_hex(),
2149 "name": function.name,
2150 "file": function.file.display().to_string(),
2151 "location": {
2152 "start_line": function.span.start_line,
2153 "end_line": function.span.end_line
2154 }
2155 },
2156 "description": format!("Function '{}' has high complexity ({} lines, {} dependencies)", function.name, function_lines, dependencies.len()),
2157 "estimated_complexity": "O(n^2) or worse",
2158 "recommendation": "Break down into smaller functions and optimize algorithms",
2159 "lines_of_code": function_lines,
2160 "dependency_count": dependencies.len()
2161 }));
2162 }
2163 }
2164 }
2165 }
2166
2167 Ok(issues)
2168}
2169
2170async fn analyze_memory_usage(
2172 server: &CodePrismMcpServer,
2173 exclude_patterns: &[String],
2174) -> Result<Vec<serde_json::Value>> {
2175 let mut issues = Vec::new();
2176 let functions = server
2177 .graph_store()
2178 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2179
2180 for function in functions {
2181 if exclude_patterns
2182 .iter()
2183 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2184 {
2185 continue;
2186 }
2187
2188 let function_name_lower = function.name.to_lowercase();
2189
2190 if function_name_lower.contains("load")
2192 || function_name_lower.contains("read")
2193 || function_name_lower.contains("parse")
2194 || function_name_lower.contains("create")
2195 || function_name_lower.contains("build")
2196 {
2197 issues.push(serde_json::json!({
2198 "type": "Memory Usage",
2199 "category": "memory_usage",
2200 "severity": "medium",
2201 "complexity": "medium",
2202 "function": {
2203 "id": function.id.to_hex(),
2204 "name": function.name,
2205 "file": function.file.display().to_string(),
2206 "location": {
2207 "start_line": function.span.start_line,
2208 "end_line": function.span.end_line
2209 }
2210 },
2211 "description": format!("Function '{}' may consume significant memory", function.name),
2212 "recommendation": "Consider streaming, pagination, or memory pooling strategies",
2213 "impact": "Potential memory pressure with large inputs"
2214 }));
2215 }
2216
2217 if function_name_lower.contains("alloc")
2219 || function_name_lower.contains("new")
2220 || function_name_lower.contains("create")
2221 {
2222 let all_functions = server
2224 .graph_store()
2225 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2226 let has_cleanup = all_functions.iter().any(|f| {
2227 let cleanup_name = f.name.to_lowercase();
2228 cleanup_name.contains("free")
2229 || cleanup_name.contains("delete")
2230 || cleanup_name.contains("dispose")
2231 || cleanup_name.contains("close")
2232 });
2233
2234 if !has_cleanup {
2235 issues.push(serde_json::json!({
2236 "type": "Potential Memory Leak",
2237 "category": "memory_usage",
2238 "severity": "high",
2239 "complexity": "high",
2240 "function": {
2241 "id": function.id.to_hex(),
2242 "name": function.name,
2243 "file": function.file.display().to_string(),
2244 "location": {
2245 "start_line": function.span.start_line,
2246 "end_line": function.span.end_line
2247 }
2248 },
2249 "description": format!("Function '{}' allocates resources but no cleanup functions found", function.name),
2250 "recommendation": "Ensure proper resource cleanup and consider RAII patterns",
2251 "impact": "Potential memory leaks and resource exhaustion"
2252 }));
2253 }
2254 }
2255 }
2256
2257 Ok(issues)
2258}
2259
2260async fn detect_performance_hot_spots(
2262 server: &CodePrismMcpServer,
2263 exclude_patterns: &[String],
2264 detect_bottlenecks: bool,
2265) -> Result<Vec<serde_json::Value>> {
2266 let mut hot_spots = Vec::new();
2267 let functions = server
2268 .graph_store()
2269 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2270
2271 for function in functions {
2272 if exclude_patterns
2273 .iter()
2274 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2275 {
2276 continue;
2277 }
2278
2279 let references = server.graph_query().find_references(&function.id)?;
2281 let call_count = references
2282 .iter()
2283 .filter(|r| matches!(r.edge_kind, codeprism_core::EdgeKind::Calls))
2284 .count();
2285
2286 if call_count > 10 {
2287 hot_spots.push(serde_json::json!({
2288 "type": "High Call Frequency",
2289 "category": "hot_spots",
2290 "severity": "medium",
2291 "complexity": "medium",
2292 "function": {
2293 "id": function.id.to_hex(),
2294 "name": function.name,
2295 "file": function.file.display().to_string(),
2296 "location": {
2297 "start_line": function.span.start_line,
2298 "end_line": function.span.end_line
2299 }
2300 },
2301 "description": format!("Function '{}' is called {} times, making it a potential hot spot", function.name, call_count),
2302 "call_count": call_count,
2303 "recommendation": "Optimize this function as it's frequently used",
2304 "impact": "Performance improvements here will have broad impact"
2305 }));
2306 }
2307
2308 if detect_bottlenecks {
2309 let dependencies = server
2311 .graph_query()
2312 .find_dependencies(&function.id, codeprism_core::graph::DependencyType::Direct)?;
2313
2314 if dependencies.len() > 15 {
2315 hot_spots.push(serde_json::json!({
2316 "type": "Dependency Bottleneck",
2317 "category": "hot_spots",
2318 "severity": "high",
2319 "complexity": "high",
2320 "function": {
2321 "id": function.id.to_hex(),
2322 "name": function.name,
2323 "file": function.file.display().to_string(),
2324 "location": {
2325 "start_line": function.span.start_line,
2326 "end_line": function.span.end_line
2327 }
2328 },
2329 "description": format!("Function '{}' has {} dependencies, creating a potential bottleneck", function.name, dependencies.len()),
2330 "dependency_count": dependencies.len(),
2331 "recommendation": "Refactor to reduce dependencies and improve modularity",
2332 "impact": "High coupling may impact performance and maintainability"
2333 }));
2334 }
2335 }
2336 }
2337
2338 Ok(hot_spots)
2339}
2340
2341async fn detect_performance_anti_patterns(
2343 server: &CodePrismMcpServer,
2344 exclude_patterns: &[String],
2345) -> Result<Vec<serde_json::Value>> {
2346 let mut anti_patterns = Vec::new();
2347 let functions = server
2348 .graph_store()
2349 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2350
2351 for function in functions {
2352 if exclude_patterns
2353 .iter()
2354 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2355 {
2356 continue;
2357 }
2358
2359 let function_name_lower = function.name.to_lowercase();
2360
2361 if function_name_lower.contains("get")
2363 && (function_name_lower.contains("all")
2364 || function_name_lower.contains("list")
2365 || function_name_lower.contains("each"))
2366 {
2367 let dependencies = server
2369 .graph_query()
2370 .find_dependencies(&function.id, codeprism_core::graph::DependencyType::Calls)?;
2371 let db_calls = dependencies
2372 .iter()
2373 .filter(|d| {
2374 let dep_name = d.target_node.name.to_lowercase();
2375 dep_name.contains("query")
2376 || dep_name.contains("select")
2377 || dep_name.contains("find")
2378 || dep_name.contains("get")
2379 })
2380 .count();
2381
2382 if db_calls > 3 {
2383 anti_patterns.push(serde_json::json!({
2384 "type": "Potential N+1 Query",
2385 "category": "anti_patterns",
2386 "severity": "high",
2387 "complexity": "high",
2388 "function": {
2389 "id": function.id.to_hex(),
2390 "name": function.name,
2391 "file": function.file.display().to_string(),
2392 "location": {
2393 "start_line": function.span.start_line,
2394 "end_line": function.span.end_line
2395 }
2396 },
2397 "description": format!("Function '{}' may be executing N+1 queries ({} database calls)", function.name, db_calls),
2398 "database_calls": db_calls,
2399 "recommendation": "Use eager loading, batch queries, or joins to reduce database calls",
2400 "impact": "Exponential performance degradation with dataset size"
2401 }));
2402 }
2403 }
2404
2405 if function_name_lower.contains("sync")
2407 || function_name_lower.contains("block")
2408 || function_name_lower.contains("wait")
2409 {
2410 anti_patterns.push(serde_json::json!({
2411 "type": "Synchronous Blocking",
2412 "category": "anti_patterns",
2413 "severity": "medium",
2414 "complexity": "medium",
2415 "function": {
2416 "id": function.id.to_hex(),
2417 "name": function.name,
2418 "file": function.file.display().to_string(),
2419 "location": {
2420 "start_line": function.span.start_line,
2421 "end_line": function.span.end_line
2422 }
2423 },
2424 "description": format!("Function '{}' may contain blocking operations", function.name),
2425 "recommendation": "Consider using asynchronous operations to improve responsiveness",
2426 "impact": "May block execution and reduce system throughput"
2427 }));
2428 }
2429
2430 if function_name_lower.contains("concat")
2432 || function_name_lower.contains("join")
2433 || function_name_lower.contains("append")
2434 {
2435 anti_patterns.push(serde_json::json!({
2436 "type": "String Concatenation",
2437 "category": "anti_patterns",
2438 "severity": "low",
2439 "complexity": "low",
2440 "function": {
2441 "id": function.id.to_hex(),
2442 "name": function.name,
2443 "file": function.file.display().to_string(),
2444 "location": {
2445 "start_line": function.span.start_line,
2446 "end_line": function.span.end_line
2447 }
2448 },
2449 "description": format!("Function '{}' may be performing inefficient string operations", function.name),
2450 "recommendation": "Use StringBuilder, string templates, or buffer-based operations",
2451 "impact": "Quadratic performance with string size in some languages"
2452 }));
2453 }
2454 }
2455
2456 Ok(anti_patterns)
2457}
2458
2459async fn analyze_scalability_concerns(
2461 server: &CodePrismMcpServer,
2462 exclude_patterns: &[String],
2463) -> Result<Vec<serde_json::Value>> {
2464 let mut concerns = Vec::new();
2465 let functions = server
2466 .graph_store()
2467 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2468
2469 for function in functions {
2470 if exclude_patterns
2471 .iter()
2472 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2473 {
2474 continue;
2475 }
2476
2477 let function_name_lower = function.name.to_lowercase();
2478
2479 if function_name_lower.contains("global")
2481 || function_name_lower.contains("singleton")
2482 || function_name_lower.contains("static")
2483 {
2484 concerns.push(serde_json::json!({
2485 "type": "Global State Usage",
2486 "category": "scalability",
2487 "severity": "medium",
2488 "complexity": "medium",
2489 "function": {
2490 "id": function.id.to_hex(),
2491 "name": function.name,
2492 "file": function.file.display().to_string(),
2493 "location": {
2494 "start_line": function.span.start_line,
2495 "end_line": function.span.end_line
2496 }
2497 },
2498 "description": format!("Function '{}' may use global state", function.name),
2499 "recommendation": "Reduce global state dependency for better scalability",
2500 "impact": "Global state can limit horizontal scaling and cause race conditions"
2501 }));
2502 }
2503
2504 if function_name_lower.contains("file")
2506 || function_name_lower.contains("disk")
2507 || function_name_lower.contains("write")
2508 || function_name_lower.contains("read")
2509 {
2510 concerns.push(serde_json::json!({
2511 "type": "File System Dependency",
2512 "category": "scalability",
2513 "severity": "low",
2514 "complexity": "low",
2515 "function": {
2516 "id": function.id.to_hex(),
2517 "name": function.name,
2518 "file": function.file.display().to_string(),
2519 "location": {
2520 "start_line": function.span.start_line,
2521 "end_line": function.span.end_line
2522 }
2523 },
2524 "description": format!("Function '{}' may have file system dependencies", function.name),
2525 "recommendation": "Consider using distributed storage or caching for scalability",
2526 "impact": "File system operations may not scale in distributed environments"
2527 }));
2528 }
2529 }
2530
2531 Ok(concerns)
2532}
2533
2534fn calculate_performance_score(issues: &[serde_json::Value]) -> u32 {
2536 if issues.is_empty() {
2537 return 100;
2538 }
2539
2540 let mut score = 100;
2541 let critical_count = issues
2542 .iter()
2543 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("critical"))
2544 .count();
2545 let high_count = issues
2546 .iter()
2547 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("high"))
2548 .count();
2549 let medium_count = issues
2550 .iter()
2551 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("medium"))
2552 .count();
2553
2554 score -= critical_count * 20;
2556 score -= high_count * 10;
2557 score -= medium_count * 5;
2558
2559 score.max(0) as u32
2561}
2562
2563fn get_performance_recommendations(issues: &[serde_json::Value]) -> Vec<String> {
2565 let mut recommendations = Vec::new();
2566
2567 let time_complexity_count = issues
2568 .iter()
2569 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("time_complexity"))
2570 .count();
2571
2572 if time_complexity_count > 0 {
2573 recommendations.push(format!(
2574 "Optimize {} algorithms with high time complexity using more efficient data structures",
2575 time_complexity_count
2576 ));
2577 }
2578
2579 let memory_count = issues
2580 .iter()
2581 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("memory_usage"))
2582 .count();
2583
2584 if memory_count > 0 {
2585 recommendations.push(format!(
2586 "Address {} memory usage issues with streaming, pagination, or caching strategies",
2587 memory_count
2588 ));
2589 }
2590
2591 let hot_spots_count = issues
2592 .iter()
2593 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("hot_spots"))
2594 .count();
2595
2596 if hot_spots_count > 0 {
2597 recommendations.push(format!(
2598 "Focus optimization efforts on {} identified performance hot spots",
2599 hot_spots_count
2600 ));
2601 }
2602
2603 let anti_patterns_count = issues
2604 .iter()
2605 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("anti_patterns"))
2606 .count();
2607
2608 if anti_patterns_count > 0 {
2609 recommendations.push(format!(
2610 "Refactor {} performance anti-patterns to improve scalability",
2611 anti_patterns_count
2612 ));
2613 }
2614
2615 let scalability_count = issues
2616 .iter()
2617 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("scalability"))
2618 .count();
2619
2620 if scalability_count > 0 {
2621 recommendations.push(format!(
2622 "Address {} scalability concerns by reducing global state and blocking operations",
2623 scalability_count
2624 ));
2625 }
2626
2627 if recommendations.is_empty() {
2628 recommendations
2629 .push("No significant performance issues detected with current analysis".to_string());
2630 } else {
2631 recommendations.push("Use profiling tools to validate performance assumptions".to_string());
2632 recommendations.push("Implement performance monitoring and alerting".to_string());
2633 recommendations
2634 .push("Consider load testing to validate scalability improvements".to_string());
2635 }
2636
2637 recommendations
2638}
2639
2640async fn analyze_public_api(
2642 server: &CodePrismMcpServer,
2643 exclude_patterns: &[String],
2644 include_private_apis: bool,
2645) -> Result<Vec<serde_json::Value>> {
2646 let mut issues = Vec::new();
2647 let functions = server
2648 .graph_store()
2649 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2650 let classes = server
2651 .graph_store()
2652 .get_nodes_by_kind(codeprism_core::NodeKind::Class);
2653
2654 for function in functions {
2656 if exclude_patterns
2657 .iter()
2658 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2659 {
2660 continue;
2661 }
2662
2663 let function_name = &function.name;
2664 let is_public = is_public_api_element(function_name);
2665 let is_private = function_name.starts_with('_') || function_name.contains("private");
2666
2667 if is_public || (include_private_apis && is_private) {
2668 let references = server.graph_query().find_references(&function.id)?;
2669 let external_usage_count = references.len();
2670
2671 issues.push(serde_json::json!({
2672 "type": if is_public { "Public API Function" } else { "Private API Function" },
2673 "category": "public_api",
2674 "severity": if is_public { "medium" } else { "low" },
2675 "function": {
2676 "id": function.id.to_hex(),
2677 "name": function.name,
2678 "file": function.file.display().to_string(),
2679 "location": {
2680 "start_line": function.span.start_line,
2681 "end_line": function.span.end_line
2682 }
2683 },
2684 "description": format!("Function '{}' is part of the {} API surface", function.name, if is_public { "public" } else { "private" }),
2685 "visibility": if is_public { "public" } else { "private" },
2686 "external_usage_count": external_usage_count,
2687 "recommendation": if is_public { "Ensure this function is well-documented and maintains backward compatibility" } else { "Consider if this function should be exposed or kept internal" }
2688 }));
2689 }
2690 }
2691
2692 for class in classes {
2694 if exclude_patterns
2695 .iter()
2696 .any(|pattern| class.file.to_string_lossy().contains(pattern))
2697 {
2698 continue;
2699 }
2700
2701 let class_name = &class.name;
2702 let is_public = is_public_api_element(class_name);
2703 let is_private = class_name.starts_with('_') || class_name.contains("private");
2704
2705 if is_public || (include_private_apis && is_private) {
2706 let references = server.graph_query().find_references(&class.id)?;
2707 let external_usage_count = references.len();
2708
2709 issues.push(serde_json::json!({
2710 "type": if is_public { "Public API Class" } else { "Private API Class" },
2711 "category": "public_api",
2712 "severity": if is_public { "medium" } else { "low" },
2713 "class": {
2714 "id": class.id.to_hex(),
2715 "name": class.name,
2716 "file": class.file.display().to_string(),
2717 "location": {
2718 "start_line": class.span.start_line,
2719 "end_line": class.span.end_line
2720 }
2721 },
2722 "description": format!("Class '{}' is part of the {} API surface", class.name, if is_public { "public" } else { "private" }),
2723 "visibility": if is_public { "public" } else { "private" },
2724 "external_usage_count": external_usage_count,
2725 "recommendation": if is_public { "Ensure this class provides a stable interface and is well-documented" } else { "Consider if this class should be part of the public API" }
2726 }));
2727 }
2728 }
2729
2730 Ok(issues)
2731}
2732
2733async fn analyze_api_versioning(
2735 server: &CodePrismMcpServer,
2736 exclude_patterns: &[String],
2737 api_version: Option<&str>,
2738) -> Result<Vec<serde_json::Value>> {
2739 let mut issues = Vec::new();
2740 let functions = server
2741 .graph_store()
2742 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2743
2744 for function in functions {
2745 if exclude_patterns
2746 .iter()
2747 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2748 {
2749 continue;
2750 }
2751
2752 if is_public_api_element(&function.name) {
2753 let function_name_lower = function.name.to_lowercase();
2754
2755 if function_name_lower.contains("v1")
2757 || function_name_lower.contains("v2")
2758 || function_name_lower.contains("version")
2759 {
2760 issues.push(serde_json::json!({
2761 "type": "Versioned API",
2762 "category": "versioning",
2763 "severity": "low",
2764 "function": {
2765 "id": function.id.to_hex(),
2766 "name": function.name,
2767 "file": function.file.display().to_string(),
2768 "location": {
2769 "start_line": function.span.start_line,
2770 "end_line": function.span.end_line
2771 }
2772 },
2773 "description": format!("Function '{}' appears to be version-specific", function.name),
2774 "current_version": api_version.unwrap_or("unknown"),
2775 "recommendation": "Ensure version consistency and provide migration paths for deprecated versions"
2776 }));
2777 }
2778
2779 if function_name_lower.contains("deprecated")
2781 || function_name_lower.contains("legacy")
2782 || function_name_lower.contains("old")
2783 {
2784 issues.push(serde_json::json!({
2785 "type": "Deprecated API",
2786 "category": "versioning",
2787 "severity": "high",
2788 "function": {
2789 "id": function.id.to_hex(),
2790 "name": function.name,
2791 "file": function.file.display().to_string(),
2792 "location": {
2793 "start_line": function.span.start_line,
2794 "end_line": function.span.end_line
2795 }
2796 },
2797 "description": format!("Function '{}' appears to be deprecated", function.name),
2798 "recommendation": "Provide clear deprecation timeline and migration path to new API"
2799 }));
2800 }
2801 }
2802 }
2803
2804 Ok(issues)
2805}
2806
2807async fn detect_api_breaking_changes(
2809 server: &CodePrismMcpServer,
2810 exclude_patterns: &[String],
2811) -> Result<Vec<serde_json::Value>> {
2812 let mut issues = Vec::new();
2813 let functions = server
2814 .graph_store()
2815 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2816
2817 for function in functions {
2818 if exclude_patterns
2819 .iter()
2820 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2821 {
2822 continue;
2823 }
2824
2825 if is_public_api_element(&function.name) {
2826 let dependencies = server
2827 .graph_query()
2828 .find_dependencies(&function.id, codeprism_core::graph::DependencyType::Direct)?;
2829
2830 if dependencies.len() > 10 {
2832 issues.push(serde_json::json!({
2833 "type": "Breaking Change Risk",
2834 "category": "breaking_changes",
2835 "severity": "medium",
2836 "function": {
2837 "id": function.id.to_hex(),
2838 "name": function.name,
2839 "file": function.file.display().to_string(),
2840 "location": {
2841 "start_line": function.span.start_line,
2842 "end_line": function.span.end_line
2843 }
2844 },
2845 "description": format!("Function '{}' has many dependencies ({}) which increases breaking change risk", function.name, dependencies.len()),
2846 "dependency_count": dependencies.len(),
2847 "recommendation": "Consider interface stability and impact assessment before changes"
2848 }));
2849 }
2850
2851 let function_name_lower = function.name.to_lowercase();
2853 if function_name_lower.contains("delete")
2854 || function_name_lower.contains("remove")
2855 || function_name_lower.contains("drop")
2856 {
2857 issues.push(serde_json::json!({
2858 "type": "Potential Breaking Change",
2859 "category": "breaking_changes",
2860 "severity": "high",
2861 "function": {
2862 "id": function.id.to_hex(),
2863 "name": function.name,
2864 "file": function.file.display().to_string(),
2865 "location": {
2866 "start_line": function.span.start_line,
2867 "end_line": function.span.end_line
2868 }
2869 },
2870 "description": format!("Function '{}' may introduce breaking changes due to destructive operations", function.name),
2871 "recommendation": "Ensure proper versioning and deprecation strategy for breaking changes"
2872 }));
2873 }
2874 }
2875 }
2876
2877 Ok(issues)
2878}
2879
2880async fn analyze_api_documentation_coverage(
2882 server: &CodePrismMcpServer,
2883 exclude_patterns: &[String],
2884) -> Result<Vec<serde_json::Value>> {
2885 let mut issues = Vec::new();
2886 let functions = server
2887 .graph_store()
2888 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2889 let classes = server
2890 .graph_store()
2891 .get_nodes_by_kind(codeprism_core::NodeKind::Class);
2892
2893 for function in functions {
2895 if exclude_patterns
2896 .iter()
2897 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2898 {
2899 continue;
2900 }
2901
2902 if is_public_api_element(&function.name) {
2903 let has_documentation = function
2905 .metadata
2906 .get("documentation")
2907 .and_then(|d| d.as_str())
2908 .map(|s| !s.is_empty())
2909 .unwrap_or(false);
2910
2911 if !has_documentation {
2912 issues.push(serde_json::json!({
2913 "type": "Undocumented API",
2914 "category": "documentation_coverage",
2915 "severity": "medium",
2916 "function": {
2917 "id": function.id.to_hex(),
2918 "name": function.name,
2919 "file": function.file.display().to_string(),
2920 "location": {
2921 "start_line": function.span.start_line,
2922 "end_line": function.span.end_line
2923 }
2924 },
2925 "description": format!("Public function '{}' lacks documentation", function.name),
2926 "recommendation": "Add comprehensive documentation including parameters, return values, and usage examples"
2927 }));
2928 }
2929 }
2930 }
2931
2932 for class in classes {
2934 if exclude_patterns
2935 .iter()
2936 .any(|pattern| class.file.to_string_lossy().contains(pattern))
2937 {
2938 continue;
2939 }
2940
2941 if is_public_api_element(&class.name) {
2942 let has_documentation = class
2943 .metadata
2944 .get("documentation")
2945 .and_then(|d| d.as_str())
2946 .map(|s| !s.is_empty())
2947 .unwrap_or(false);
2948
2949 if !has_documentation {
2950 issues.push(serde_json::json!({
2951 "type": "Undocumented API Class",
2952 "category": "documentation_coverage",
2953 "severity": "medium",
2954 "class": {
2955 "id": class.id.to_hex(),
2956 "name": class.name,
2957 "file": class.file.display().to_string(),
2958 "location": {
2959 "start_line": class.span.start_line,
2960 "end_line": class.span.end_line
2961 }
2962 },
2963 "description": format!("Public class '{}' lacks documentation", class.name),
2964 "recommendation": "Add class documentation including purpose, usage patterns, and example usage"
2965 }));
2966 }
2967 }
2968 }
2969
2970 Ok(issues)
2971}
2972
2973async fn analyze_api_compatibility(
2975 server: &CodePrismMcpServer,
2976 exclude_patterns: &[String],
2977 api_version: Option<&str>,
2978) -> Result<Vec<serde_json::Value>> {
2979 let mut issues = Vec::new();
2980 let functions = server
2981 .graph_store()
2982 .get_nodes_by_kind(codeprism_core::NodeKind::Function);
2983
2984 for function in functions {
2985 if exclude_patterns
2986 .iter()
2987 .any(|pattern| function.file.to_string_lossy().contains(pattern))
2988 {
2989 continue;
2990 }
2991
2992 if is_public_api_element(&function.name) {
2993 let function_name_lower = function.name.to_lowercase();
2994
2995 if function_name_lower.contains("experimental")
2997 || function_name_lower.contains("unstable")
2998 || function_name_lower.contains("beta")
2999 || function_name_lower.contains("alpha")
3000 {
3001 issues.push(serde_json::json!({
3002 "type": "Unstable API",
3003 "category": "compatibility",
3004 "severity": "medium",
3005 "function": {
3006 "id": function.id.to_hex(),
3007 "name": function.name,
3008 "file": function.file.display().to_string(),
3009 "location": {
3010 "start_line": function.span.start_line,
3011 "end_line": function.span.end_line
3012 }
3013 },
3014 "description": format!("Function '{}' appears to be experimental or unstable", function.name),
3015 "api_version": api_version.unwrap_or("unknown"),
3016 "recommendation": "Clearly document stability guarantees and provide stable alternatives"
3017 }));
3018 }
3019
3020 if function_name_lower.contains("linux")
3022 || function_name_lower.contains("windows")
3023 || function_name_lower.contains("mac")
3024 || function_name_lower.contains("android")
3025 || function_name_lower.contains("ios")
3026 {
3027 issues.push(serde_json::json!({
3028 "type": "Platform-Specific API",
3029 "category": "compatibility",
3030 "severity": "low",
3031 "function": {
3032 "id": function.id.to_hex(),
3033 "name": function.name,
3034 "file": function.file.display().to_string(),
3035 "location": {
3036 "start_line": function.span.start_line,
3037 "end_line": function.span.end_line
3038 }
3039 },
3040 "description": format!("Function '{}' appears to be platform-specific", function.name),
3041 "recommendation": "Provide cross-platform alternatives or clear platform requirements"
3042 }));
3043 }
3044 }
3045 }
3046
3047 Ok(issues)
3048}
3049
3050fn is_public_api_element(name: &str) -> bool {
3052 !name.starts_with('_') && !name.contains("internal") && !name.contains("private") && !name.contains("test") && !name.contains("debug") && !name.contains("mock") }
3060
3061fn calculate_api_health_score(issues: &[serde_json::Value]) -> u32 {
3063 if issues.is_empty() {
3064 return 100;
3065 }
3066
3067 let mut score = 100;
3068 let critical_count = issues
3069 .iter()
3070 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("critical"))
3071 .count();
3072 let high_count = issues
3073 .iter()
3074 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("high"))
3075 .count();
3076 let medium_count = issues
3077 .iter()
3078 .filter(|i| i.get("severity").and_then(|s| s.as_str()) == Some("medium"))
3079 .count();
3080
3081 score -= critical_count * 25;
3083 score -= high_count * 15;
3084 score -= medium_count * 5;
3085
3086 score.max(0) as u32
3088}
3089
3090fn get_api_recommendations(issues: &[serde_json::Value]) -> Vec<String> {
3092 let mut recommendations = Vec::new();
3093
3094 let public_api_count = issues
3095 .iter()
3096 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("public_api"))
3097 .count();
3098
3099 if public_api_count > 0 {
3100 recommendations.push(format!(
3101 "Review {} public API elements for stability and documentation",
3102 public_api_count
3103 ));
3104 }
3105
3106 let versioning_count = issues
3107 .iter()
3108 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("versioning"))
3109 .count();
3110
3111 if versioning_count > 0 {
3112 recommendations.push(format!(
3113 "Address {} versioning issues with proper deprecation strategies",
3114 versioning_count
3115 ));
3116 }
3117
3118 let breaking_changes_count = issues
3119 .iter()
3120 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("breaking_changes"))
3121 .count();
3122
3123 if breaking_changes_count > 0 {
3124 recommendations.push(format!(
3125 "Assess {} potential breaking changes and implement proper migration paths",
3126 breaking_changes_count
3127 ));
3128 }
3129
3130 let documentation_count = issues
3131 .iter()
3132 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("documentation_coverage"))
3133 .count();
3134
3135 if documentation_count > 0 {
3136 recommendations.push(format!(
3137 "Improve documentation for {} undocumented API elements",
3138 documentation_count
3139 ));
3140 }
3141
3142 let compatibility_count = issues
3143 .iter()
3144 .filter(|i| i.get("category").and_then(|c| c.as_str()) == Some("compatibility"))
3145 .count();
3146
3147 if compatibility_count > 0 {
3148 recommendations.push(format!(
3149 "Address {} compatibility concerns for better cross-platform support",
3150 compatibility_count
3151 ));
3152 }
3153
3154 if recommendations.is_empty() {
3155 recommendations.push("API surface analysis shows healthy API design".to_string());
3156 } else {
3157 recommendations.push("Implement semantic versioning for better API evolution".to_string());
3158 recommendations.push("Establish API design guidelines and review processes".to_string());
3159 recommendations.push("Consider API backwards compatibility testing".to_string());
3160 }
3161
3162 recommendations
3163}