18.2 程式碼審查技能
程式碼審查技能概述
程式碼審查技能是 Claude Code Skills 中用於自動化程式碼審查的重要工具。它可以幫助開發者快速識別程式碼中的問題,提高程式碼質量和可維護性。
python
## 审查类型
### 1\. 静态代码分析
#### 1.1 代码质量检查
python
# src/skills/code_reviewer.py
from typing import Dict, Any, List
from claude_code_sdk import Skill, SkillContext, SkillResult
import re
class CodeReviewerSkill(Skill):
"""代码审查技能"""
def __init__(self):
super().__init__(
name="code-reviewer",
version="1.0.0",
description="Automated code review skill"
)
# 定义审查规则
self.rules = {
"naming": self.check_naming_conventions,
"complexity": self.check_complexity,
"security": self.check_security,
"performance": self.check_performance,
"documentation": self.check_documentation
}
def get_parameters_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to the file to review"
},
"code": {
"type": "string",
"description": "Code to review (alternative to file_path)"
},
"language": {
"type": "string",
"enum": ["python", "javascript", "java", "go"],
"description": "Programming language"
},
"rules": {
"type": "array",
"description": "Rules to apply",
"items": {
"type": "string",
"enum": ["naming", "complexity", "security", "performance", "documentation"]
}
},
"severity": {
"type": "string",
"enum": ["all", "error", "warning", "info"],
"description": "Minimum severity level"
}
},
"required": ["language"]
}
def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult:
try:
language = parameters["language"]
rules_to_apply = parameters.get("rules", list(self.rules.keys()))
severity = parameters.get("severity", "all")
# 获取代码
if "file_path" in parameters:
code = context.read_file(parameters["file_path"])
file_path = parameters["file_path"]
elif "code" in parameters:
code = parameters["code"]
file_path = "<inline>"
else:
return SkillResult(
success=False,
error="Either file_path or code must be provided"
)
# 执行审查
issues = []
for rule_name in rules_to_apply:
if rule_name in self.rules:
rule_func = self.rules[rule_name]
rule_issues = rule_func(code, language)
issues.extend(rule_issues)
# 过滤严重性
if severity != "all":
severity_levels = {"error": 3, "warning": 2, "info": 1}
min_level = severity_levels.get(severity, 0)
issues = [
issue for issue in issues
if severity_levels.get(issue["severity"], 0) >= min_level
]
# 生成报告
report = self.generate_report(code, issues, file_path)
return SkillResult(
success=True,
data={
"file_path": file_path,
"language": language,
"rules_applied": rules_to_apply,
"issues": issues,
"issue_count": len(issues),
"report": report
}
)
except Exception as e:
return SkillResult(
success=False,
error=str(e)
)
def check_naming_conventions(self, code: str, language: str) -> List[Dict]:
"""检查命名规范"""
issues = []
if language == "python":
# 检查函数名(应该是 snake_case)
func_pattern = r'def\s+([A-Z][a-zA-Z0-9_]*)\s*\('
for match in re.finditer(func_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "naming",
"severity": "warning",
"line": line_num,
"message": f"Function name '{match.group(1)}' should use snake_case",
"suggestion": match.group(1).lower()
})
# 检查类名(应该是 CamelCase)
class_pattern = r'class\s+([a-z][a-zA-Z0-9_]*)\s*[:\(]'
for match in re.finditer(class_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "naming",
"severity": "warning",
"line": line_num,
"message": f"Class name '{match.group(1)}' should use CamelCase",
"suggestion": match.group(1).title()
})
elif language == "javascript":
# 检查常量(应该是 UPPER_CASE)
const_pattern = r'const\s+([a-z][a-zA-Z0-9_]*)\s*='
for match in re.finditer(const_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "naming",
"severity": "info",
"line": line_num,
"message": f"Constant '{match.group(1)}' should use UPPER_CASE",
"suggestion": match.group(1).upper()
})
return issues
def check_complexity(self, code: str, language: str) -> List[Dict]:
"""检查复杂度"""
issues = []
lines = code.split('\n')
for i, line in enumerate(lines, 1):
# 计算缩进级别
indent = len(line) - len(line.lstrip())
# 检查过深的嵌套
if indent > 24:
issues.append({
"type": "complexity",
"severity": "warning",
"line": i,
"message": f"Deep nesting detected (indent level: {indent // 4})",
"suggestion": "Consider refactoring to reduce nesting"
})
# 检查长行
if len(line) > 120:
issues.append({
"type": "complexity",
"severity": "info",
"line": i,
"message": f"Line too long ({len(line)} characters)",
"suggestion": "Break the line into multiple lines"
})
return issues
```python
def check_security(self, code: str, language: str) -> List[Dict]:
"""檢查安全問題"""
issues = []
```python
if language == "python":
# 检查 eval 使用
eval_pattern = r'\beval\s*\('
for match in re.finditer(eval_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "security",
"severity": "error",
"line": line_num,
"message": "Use of eval() is dangerous",
"suggestion": "Use ast.literal_eval() or alternative safe methods"
})
# 检查 exec 使用
exec_pattern = r'\bexec\s*\('
for match in re.finditer(exec_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "security",
"severity": "error",
"line": line_num,
"message": "Use of exec() is dangerous",
"suggestion": "Avoid using exec() for security reasons"
})
# 检查硬编码密码
password_pattern = r'(password|passwd|pwd)\s*=\s*["\'][^"\']+["\']'
for match in re.finditer(password_pattern, code, re.IGNORECASE):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "security",
"severity": "warning",
"line": line_num,
"message": "Hardcoded password detected",
"suggestion": "Use environment variables or configuration files"
})
elif language == "javascript":
# 检查 innerHTML 使用
innerhtml_pattern = r'\.innerHTML\s*='
for match in re.finditer(innerhtml_pattern, code):
line_num = code[:match.start()].count('\n') + 1
issues.append({
"type": "security",
"severity": "warning",
"line": line_num,
"message": "Use of innerHTML can lead to XSS vulnerabilities",
"suggestion": "Use textContent or sanitize input"
})
return issues
def check_performance(self, code: str, language: str) -> List[Dict]:
"""检查性能问题"""
issues = []
if language == "python":
# 检查循环中的字符串拼接
lines = code.split('\n')
in_loop = False
loop_indent = 0
for i, line in enumerate(lines, 1):
# 检测循环
if re.match(r'\s*(for|while)\s+', line):
in_loop = True
loop_indent = len(line) - len(line.lstrip())
elif in_loop and len(line) - len(line.lstrip()) <= loop_indent:
in_loop = False
# 检查字符串拼接
if in_loop and '+=' in line and '"' in line and "'" in line:
issues.append({
"type": "performance",
"severity": "warning",
"line": i,
"message": "String concatenation in loop may be inefficient",
"suggestion": "Use list and join() for better performance"
})
return issues
def check_documentation(self, code: str, language: str) -> List[Dict]:
"""检查文档"""
issues = []
if language == "python":
# 检查函数是否有文档字符串
func_pattern = r'def\s+(\w+)\s*\([^)]*\):'
for match in re.finditer(func_pattern, code):
func_name = match.group(1)
func_start = match.end()
# 检查下一行是否有文档字符串
remaining_code = code[func_start:func_start + 100] if not re.search(r'\s*"""', remaining_code): line_num = code[:match.start()].count('\n') + 1 issues.append({ "type": "documentation", "severity": "info", "line": line_num, "message": f"Function '{func_name}' lacks docstring", "suggestion": "Add a docstring to describe the function" })return issues
def generate_report(self, code: str, issues: List[Dict], file_path: str) -> str: """生成审查报告""" report = f"# Code Review Report for {file_path}\n\n"
# 统计
error_count = sum(1 for i in issues if i["severity"] == "error") warning_count = sum(1 for i in issues if i["severity"] == "warning") info_count = sum(1 for i in issues if i["severity"] == "info")
report += f"## Summary\n\n" report += f"- Total Issues: {len(issues)}\n" report += f"- Errors: {error_count}\n" report += f"- Warnings: {warning_count}\n" report += f"- Info: {info_count}\n\n"
# 按类型分组
issues_by_type = {} for issue in issues: issue_type = issue["type"] if issue_type not in issues_by_type: issues_by_type[issue_type] = [] issues_by_type[issue_type].append(issue)
# 详细问题
for issue_type, type_issues in issues_by_type.items(): report += f"## {issue_type.title()} Issues ({len(type_issues)})\n\n"
for issue in type_issues: severity_icon = { "error": "❌", "warning": "⚠️", "info": "ℹ️" }.get(issue["severity"], "•")
report += f"{severity_icon} **Line {issue['line']}** : {issue['message']}\n" if "suggestion" in issue: report += f" 💡 Suggestion: {issue['suggestion']}\n" report += "\n"
return report
### 2\. 代码风格检查
#### 2.1 PEP 8 检查
bash
python
# src/skills/pep8_checker.py
from typing import Dict, Any, List
from claude_code_sdk import Skill, SkillContext, SkillResult
import re
class PEP8CheckerSkill(Skill):
"""PEP 8 代码风格检查技能"""
def __init__(self):
super().__init__(
name="pep8-checker",
version="1.0.0",
description="PEP 8 style checker"
)
def get_parameters_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to the file to check"
},
"code": {
"type": "string",
"description": "Code to check"
},
"max_line_length": {
"type": "integer",
"description": "Maximum line length",
"default": 79
}
}
}
def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult:
try:
max_line_length = parameters.get("max_line_length", 79)
# 获取代码
if "file_path" in parameters:
code = context.read_file(parameters["file_path"])
file_path = parameters["file_path"]
elif "code" in parameters:
code = parameters["code"]
file_path = "<inline>"
else:
return SkillResult(
success=False,
error="Either file_path or code must be provided"
)
# 执行检查
violations = self.check_pep8(code, max_line_length)
return SkillResult(
success=True,
data={
"file_path": file_path,
"max_line_length": max_line_length,
"violations": violations,
"violation_count": len(violations)
}
)
except Exception as e:
return SkillResult(
success=False,
error=str(e)
)
def check_pep8(self, code: str, max_line_length: int) -> List[Dict]:
"""检查 PEP 8 规范"""
violations = []
lines = code.split('\n')
for i, line in enumerate(lines, 1):
# 检查行长度
if len(line) > max_line_length:
violations.append({
"line": i,
"code": "E501",
"message": f"Line too long ({len(line)} > {max_line_length} characters)",
"severity": "warning"
})
# 检查尾随空格
if line.rstrip() != line.rstrip('\n').rstrip('\r'):
violations.append({
"line": i,
"code": "W291",
"message": "Trailing whitespace",
"severity": "warning"
})
# 检查空行
if line.strip() == "" and i < len(lines):
# 检查连续空行
if i > 1 and lines[i-2].strip() == "" and lines[i-1].strip() == "":
violations.append({
"line": i,
"code": "E303",
"message": "Too many blank lines",
"severity": "info"
})
# 检查导入顺序
if line.strip().startswith("import ") or line.strip().startswith("from "):
if i > 1 and lines[i-2].strip() and not (
lines[i-2].strip().startswith("import ") or
lines[i-2].strip().startswith("from ")
):
violations.append({
"line": i,
"code": "E402",
"message": "Module level import not at top of file",
"severity": "error"
})
return violations
### 3. 代码重复检测
#### 3.1 重复代码检查
```python
# src/skills/duplicate_detector.py
```python
from typing import Dict, Any, List, Tuple
from claude_code_sdk import Skill, SkillContext, SkillResult
import difflib
class DuplicateDetectorSkill(Skill):
"""重复代码检测技能"""
def __init__(self):
super().__init__(
name="duplicate-detector",
version="1.0.0",
description="Detect duplicate code blocks"
)
def get_parameters_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to the file to check"
},
"code": {
"type": "string",
"description": "Code to check"
},
"min_lines": {
"type": "integer",
"description": "Minimum number of lines to consider as duplicate",
"default": 5
},
"similarity_threshold": {
"type": "number",
"description": "Similarity threshold (0.0 to 1.0)",
"default": 0.8
}
}
}
def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult:
try:
min_lines = parameters.get("min_lines", 5)
similarity_threshold = parameters.get("similarity_threshold", 0.8)
# 获取代码
if "file_path" in parameters:
code = context.read_file(parameters["file_path"])
file_path = parameters["file_path"]
elif "code" in parameters:
code = parameters["code"]
file_path = "<inline>"
else:
return SkillResult(
success=False,
error="Either file_path or code must be provided"
)
# 检测重复
duplicates = self.detect_duplicates(code, min_lines, similarity_threshold)
return SkillResult(
success=True,
data={
"file_path": file_path,
"min_lines": min_lines,
"similarity_threshold": similarity_threshold,
"duplicates": duplicates,
"duplicate_count": len(duplicates)
}
)
except Exception as e:
return SkillResult(
success=False,
error=str(e)
)
def detect_duplicates(self, code: str, min_lines: int,
similarity_threshold: float) -> List[Dict]:
"""检测重复代码块"""
lines = code.split('\n')
duplicates = []
# 提取代码块
blocks = self.extract_blocks(lines, min_lines)
# 比较所有块对
for i, block1 in enumerate(blocks):
for j, block2 in enumerate(blocks):
if i >= j:
continue
similarity = self.calculate_similarity(block1["code"], block2["code"])
if similarity >= similarity_threshold:
duplicates.append({
"block1": {
"start_line": block1["start_line"],
"end_line": block1["end_line"],
"code": block1["code"]
},
"block2": {
"start_line": block2["start_line"],
"end_line": block2["end_line"],
"code": block2["code"]
},
"similarity": similarity,
"suggestion": "Consider extracting common code into a function"
})
return duplicates
def extract_blocks(self, lines: List[str], min_lines: int) -> List[Dict]:
"""提取代码块"""
blocks = []
for i in range(len(lines) - min_lines + 1):
block_code = '\n'.join(lines[i:i+min_lines])
blocks.append({
"start_line": i + 1,
"end_line": i + min_lines,
"code": block_code
})
return blocks
def calculate_similarity(self, code1: str, code2: str) -> float:
"""计算代码相似度"""
# 使用序列匹配器
matcher = difflib.SequenceMatcher(None, code1, code2)
return matcher.ratio()
## 使用示例
### 1\. 完整代码审查
bash
python
# examples/code_review.py
from skills.code_reviewer import CodeReviewerSkill
from claude_code_sdk import SkillContext
skill = CodeReviewerSkill()
context = SkillContext()
result = skill.execute(
{
"file_path": "src/main.py",
"language": "python",
"rules": ["naming", "complexity", "security", "performance", "documentation"],
"severity": "all"
},
context
)
print(f"Found {result.data['issue_count']} issues")
print(result.data["report"])
### 2\. PEP 8 检查
python
# examples/pep8_check.py
from skills.pep8_checker import PEP8CheckerSkill
from claude_code_sdk import SkillContext
skill = PEP8CheckerSkill()
context = SkillContext()
result = skill.execute(
{
"file_path": "src/main.py",
"max_line_length": 79
},
context
)
print(f"Found {result.data['violation_count']} PEP 8 violations")
for violation in result.data["violations"]:
print(f"Line {violation['line']}: {violation['message']}")
### 3\. 重复代码检测
bash
python
# examples/duplicate_detection.py
from skills.duplicate_detector import DuplicateDetectorSkill
from claude_code_sdk import SkillContext
skill = DuplicateDetectorSkill()
context = SkillContext()
result = skill.execute(
{
"file_path": "src/main.py",
"min_lines": 5,
"similarity_threshold": 0.8
},
context
)
print(f"Found {result.data['duplicate_count']} duplicate blocks")
for duplicate in result.data["duplicates"]:
print(f"Lines {duplicate['block1']['start_line']}-{duplicate['block1']['end_line']} "
f"similar to lines {duplicate['block2']['start_line']}-{duplicate['block2']['end_line']} "
f"(similarity: {duplicate['similarity']:.2f})")
## 最佳实践
### 1. 审查规则配置
#### 1. 规则优先级
- 安全规则:最高优先级
- 性能规则:高优先级
- 代码质量:中优先级
- 代码风格:低优先级
### 2. 规则定制
- 根据项目需求定制规则
- 考虑团队编码规范
- 逐步引入新规则
### 3. 规则例外
- 提供规则例外机制
- 记录例外原因
- 定期审查例外
### 2\. 审查流程
bash
markdown
#### 1. 自动审查
- 在提交前自动运行
- 集成到 CI/CD 流程
- 阻止不符合规范的代码
### 2. 人工审查
- 审查自动审查结果
- 关注复杂逻辑
- 提供建设性反馈
### 3. 持续改进
- 收集审查反馈
- 优化审查规则
- 提高审查效率
## 总结程式碼審查技能可以幫助團隊自動化程式碼審查流程,提高程式碼質量和一致性。透過合理配置審查規則和流程,可以顯著減少程式碼中的問題,提高開發效率。
在下一節中,我們將探討文件生成技能。