176 lines
5.1 KiB
Python
176 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
代码分析器
|
||
扫描代码文件,检测常见问题
|
||
"""
|
||
import json
|
||
import sys
|
||
import os
|
||
import re
|
||
from pathlib import Path
|
||
|
||
|
||
def analyze_file(file_path):
|
||
"""分析单个代码文件"""
|
||
issues = []
|
||
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||
content = f.read()
|
||
lines = content.split('\n')
|
||
|
||
# 检测模式
|
||
patterns = {
|
||
'TODO': r'TODO|FIXME|XXX|HACK',
|
||
'empty_function': r'def\s+\w+\s*\([^)]*\)\s*:\s*(pass|#\s*.*|.{0})',
|
||
'empty_method': r'(def|async\s+def)\s+\w+\s*\([^)]*\)\s*:\s*(pass|.{0})',
|
||
'commented_code': r'^\s*#.*=[^#\n]|^\s*#.*\([^)]*\)',
|
||
'placeholder': r'(NotImplementedError|raise\s+NotImplemented|pass\s*#.*placeholder)',
|
||
'debug_print': r'print\([^)]*\)|console\.log\([^)]*\)',
|
||
}
|
||
|
||
for line_num, line in enumerate(lines, 1):
|
||
for issue_type, pattern in patterns.items():
|
||
if re.search(pattern, line, re.IGNORECASE):
|
||
issues.append({
|
||
'type': issue_type,
|
||
'line': line_num,
|
||
'content': line.strip(),
|
||
'file': str(file_path)
|
||
})
|
||
|
||
except Exception as e:
|
||
issues.append({
|
||
'type': 'read_error',
|
||
'message': str(e),
|
||
'file': str(file_path)
|
||
})
|
||
|
||
return issues
|
||
|
||
|
||
def detect_project_changes(project_root):
|
||
"""检测项目中的代码变更"""
|
||
import subprocess
|
||
|
||
try:
|
||
# 获取 Git 变更的文件
|
||
result = subprocess.run(
|
||
['git', 'diff', '--name-only', 'HEAD'],
|
||
cwd=project_root,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=5
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
changed_files = result.stdout.strip().split('\n')
|
||
changed_files = [f for f in changed_files if f]
|
||
|
||
# 过滤代码文件
|
||
code_extensions = {'.py', '.js', '.ts', '.jsx', '.tsx', '.java', '.go', '.rs', '.c', '.cpp', '.h'}
|
||
code_files = [
|
||
Path(project_root) / f
|
||
for f in changed_files
|
||
if Path(f).suffix.lower() in code_extensions
|
||
]
|
||
return code_files
|
||
|
||
except Exception:
|
||
pass
|
||
|
||
# 如果 Git 不可用,扫描所有代码文件
|
||
code_extensions = {'.py', '.js', '.ts', '.jsx', '.tsx', '.java', '.go', '.rs', '.c', '.cpp', '.h'}
|
||
project_path = Path(project_root)
|
||
code_files = []
|
||
|
||
for ext in code_extensions:
|
||
code_files.extend(project_path.rglob(f'*{ext}'))
|
||
|
||
return code_files
|
||
|
||
|
||
def analyze_code_quality(project_root):
|
||
"""分析代码质量"""
|
||
all_issues = {
|
||
'todo': [],
|
||
'empty_functions': [],
|
||
'commented_code': [],
|
||
'placeholders': [],
|
||
'debug_statements': [],
|
||
'other': []
|
||
}
|
||
|
||
# 获取需要分析的文件
|
||
files_to_analyze = detect_project_changes(project_root)
|
||
|
||
for file_path in files_to_analyze:
|
||
if not file_path.exists():
|
||
continue
|
||
|
||
issues = analyze_file(file_path)
|
||
|
||
for issue in issues:
|
||
issue_type = issue['type']
|
||
|
||
if issue_type == 'TODO':
|
||
all_issues['todo'].append(issue)
|
||
elif 'empty' in issue_type:
|
||
all_issues['empty_functions'].append(issue)
|
||
elif issue_type == 'commented_code':
|
||
all_issues['commented_code'].append(issue)
|
||
elif issue_type == 'placeholder':
|
||
all_issues['placeholders'].append(issue)
|
||
elif issue_type == 'debug_print':
|
||
all_issues['debug_statements'].append(issue)
|
||
else:
|
||
all_issues['other'].append(issue)
|
||
|
||
return all_issues
|
||
|
||
|
||
def calculate_completeness_score(issues):
|
||
"""计算完整度评分"""
|
||
total_issues = sum(len(v) for v in issues.values())
|
||
|
||
# 严重问题权重
|
||
critical_issues = len(issues['empty_functions']) + len(issues['placeholders'])
|
||
minor_issues = len(issues['todo']) + len(issues['commented_code']) + len(issues['debug_statements'])
|
||
|
||
# 计算分数(0-100)
|
||
score = 100 - (critical_issues * 15) - (minor_issues * 5)
|
||
return max(0, min(100, score))
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
# 从命令行参数或 stdin 获取项目路径
|
||
if len(sys.argv) > 1:
|
||
project_root = sys.argv[1]
|
||
else:
|
||
project_root = os.getcwd()
|
||
|
||
# 执行分析
|
||
issues = analyze_code_quality(project_root)
|
||
score = calculate_completeness_score(issues)
|
||
|
||
# 输出结果
|
||
result = {
|
||
'score': score,
|
||
'issues': issues,
|
||
'total_issues': sum(len(v) for v in issues.values()),
|
||
'summary': {
|
||
'todo_count': len(issues['todo']),
|
||
'empty_functions_count': len(issues['empty_functions']),
|
||
'commented_code_count': len(issues['commented_code']),
|
||
'placeholder_count': len(issues['placeholders']),
|
||
'debug_statements_count': len(issues['debug_statements'])
|
||
}
|
||
}
|
||
|
||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|