| """ |
| Code Analysis Module |
| |
| Provides static code analysis and quality checking. |
| """ |
|
|
| from typing import Dict, List, Optional, Any, Tuple |
| import re |
|
|
|
|
| class CodeAnalyzer: |
| """Analyze code for quality, complexity, and issues.""" |
|
|
| def __init__(self): |
| """Initialize code analyzer.""" |
| pass |
|
|
| def analyze_complexity(self, code: str) -> Dict[str, Any]: |
| """ |
| Analyze code complexity. |
| |
| Returns: |
| Dictionary with complexity metrics |
| """ |
| lines = code.split('\n') |
|
|
| |
| functions = re.findall(r'def\s+(\w+)', code) |
| methods = re.findall(r'def\s+(\w+)\(self', code) |
| classes = re.findall(r'class\s+(\w+)', code) |
|
|
| |
| if_statements = len(re.findall(r'\bif\s+', code)) |
| for_loops = len(re.findall(r'\bfor\s+', code)) |
| while_loops = len(re.findall(r'\bwhile\s+', code)) |
| try_blocks = len(re.findall(r'\btry\s+', code)) |
|
|
| |
| complexity = 1 + if_statements + for_loops + while_loops + try_blocks |
|
|
| |
| loc = len([l for l in lines if l.strip() and not l.strip().startswith('#')]) |
|
|
| return { |
| "lines_of_code": loc, |
| "total_lines": len(lines), |
| "functions": len(functions), |
| "methods": len(methods), |
| "classes": len(classes), |
| "cyclomatic_complexity": complexity, |
| "if_statements": if_statements, |
| "for_loops": for_loops, |
| "while_loops": while_loops, |
| } |
|
|
| def find_issues(self, code: str, language: str = "python") -> List[Dict[str, Any]]: |
| """ |
| Find potential issues in code. |
| |
| Args: |
| code: Source code |
| language: Programming language |
| |
| Returns: |
| List of issues found |
| """ |
| issues = [] |
|
|
| |
| if language == "python": |
| issues.extend(self._check_python_issues(code)) |
|
|
| return issues |
|
|
| def _check_python_issues(self, code: str) -> List[Dict[str, Any]]: |
| """Check for Python-specific issues.""" |
| issues = [] |
|
|
| |
| for i, line in enumerate(code.split('\n'), 1): |
| if 'TODO' in line.upper() or 'FIXME' in line.upper(): |
| issues.append({ |
| "type": "todo", |
| "severity": "info", |
| "line": i, |
| "message": "TODO/FIXME comment found", |
| }) |
|
|
| |
| if re.search(r'except\s*:\s*\n\s*pass', code): |
| issues.append({ |
| "type": "empty_except", |
| "severity": "warning", |
| "message": "Empty except block - errors are silently ignored", |
| }) |
|
|
| |
| if re.search(r'password\s*=\s*["\']', code, re.IGNORECASE): |
| issues.append({ |
| "type": "hardcoded_credentials", |
| "severity": "error", |
| "message": "Potential hardcoded password found", |
| }) |
|
|
| |
| if re.search(r'\bprint\s*\(', code) and not code.startswith('# debug'): |
| issues.append({ |
| "type": "debug_print", |
| "severity": "info", |
| "message": "Print statement found - may need removal", |
| }) |
|
|
| |
| for i, line in enumerate(code.split('\n'), 1): |
| if len(line) > 120: |
| issues.append({ |
| "type": "long_line", |
| "severity": "info", |
| "line": i, |
| "message": f"Line exceeds 120 characters ({len(line)} chars)", |
| }) |
|
|
| |
| if re.search(r'^([A-Z_][A-Z0-9_]*)\s*=\s*', code, re.MULTILINE): |
| issues.append({ |
| "type": "global_variable", |
| "severity": "info", |
| "message": "Potential global variable found", |
| }) |
|
|
| return issues |
|
|
| def suggest_improvements(self, code: str, language: str = "python") -> List[str]: |
| """Suggest code improvements.""" |
| suggestions = [] |
| complexity = self.analyze_complexity(code) |
|
|
| |
| if complexity["cyclomatic_complexity"] > 10: |
| suggestions.append("High cyclomatic complexity - consider breaking into smaller functions") |
|
|
| if complexity["lines_of_code"] > 500: |
| suggestions.append("Large function - consider splitting into smaller modules") |
|
|
| |
| if "except:" in code: |
| suggestions.append("Use specific exception types instead of bare except") |
|
|
| if "print(" in code: |
| suggestions.append("Use logging instead of print statements for production code") |
|
|
| if "==" in code and "None" in code: |
| suggestions.append("Use 'is None' instead of '== None'") |
|
|
| if re.search(r'for\s+\w+\s+in\s+range\s*\(\s*len\s*\(', code): |
| suggestions.append("Use enumerate() instead of range(len())") |
|
|
| return suggestions |
|
|
| def detect_language(self, code: str) -> str: |
| """Detect programming language from code.""" |
| |
| if re.search(r'\bdef\s+\w+\s*\(', code) or re.search(r'\bimport\s+\w+', code): |
| return "python" |
|
|
| |
| if re.search(r'\bfunction\s+\w+\s*\(', code) or re.search(r'const\s+\w+\s*=', code): |
| return "javascript" |
|
|
| |
| if re.search(r'\bpublic\s+class\s+\w+', code) or re.search(r'\bSystem\.out\.print', code): |
| return "java" |
|
|
| |
| if re.search(r'\bpackage\s+main', code) or re.search(r'\bfunc\s+\w+\s*\(', code): |
| return "go" |
|
|
| |
| if re.search(r'\bfn\s+\w+\s*\(', code) or re.search(r'\blet\s+mut\s+', code): |
| return "rust" |
|
|
| |
| if re.search(r'#include\s*<', code) or re.search(r'\bint\s+main\s*\(', code): |
| return "c" |
|
|
| return "unknown" |
|
|
| def calculate_maintainability_index(self, code: str) -> float: |
| """Calculate maintainability index (0-100).""" |
| complexity = self.analyze_complexity(code) |
| loc = complexity["lines_of_code"] |
|
|
| if loc == 0: |
| return 100.0 |
|
|
| |
| |
| base = 100 |
| loc_penalty = min(loc / 100, 1) * 20 |
| complexity_penalty = min(complexity["cyclomatic_complexity"] / 20, 1) * 30 |
|
|
| index = base - loc_penalty - complexity_penalty |
| return max(0, min(100, index)) |
|
|
| def get_code_summary(self, code: str) -> Dict[str, Any]: |
| """Get comprehensive code summary.""" |
| language = self.detect_language(code) |
| complexity = self.analyze_complexity(code) |
| issues = self.find_issues(code, language) |
| suggestions = self.suggest_improvements(code, language) |
| maintainability = self.calculate_maintainability_index(code) |
|
|
| return { |
| "language": language, |
| "complexity": complexity, |
| "issues": issues, |
| "issue_count": len(issues), |
| "suggestions": suggestions, |
| "maintainability_index": maintainability, |
| } |
|
|
| def __repr__(self) -> str: |
| return "CodeAnalyzer()" |