You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
3.4 KiB

"""
Cppcheck报告解析器模块
"""
import re
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import List
from .models import CppcheckIssue, IssueLocation
def parse_cppcheck_xml(xml_path: Path) -> List[CppcheckIssue]:
"""解析cppcheck XML报告"""
tree = ET.parse(xml_path)
root = tree.getroot()
issues: List[CppcheckIssue] = []
for error in root.findall("errors/error"):
issue_id = error.get("id") or "unknown"
severity = error.get("severity") or "unknown"
msg = error.get("msg") or (error.get("verbose") or "")
locations: List[IssueLocation] = []
for loc in error.findall("location"):
file_attr = loc.get("file")
line_attr = loc.get("line")
if not file_attr:
continue
file_path = Path(file_attr).expanduser().resolve()
line = int(line_attr) if line_attr and line_attr.isdigit() else None
locations.append(IssueLocation(file_path=file_path, line=line))
if not locations:
# 有些 error 只有一层 <error file= line=>
file_attr = error.get("file")
line_attr = error.get("line")
if file_attr:
locations.append(
IssueLocation(
file_path=Path(file_attr).expanduser().resolve(),
line=int(line_attr) if line_attr and str(line_attr).isdigit() else None,
)
)
issues.append(CppcheckIssue(id=issue_id, severity=severity, message=msg, locations=locations))
return issues
def parse_cppcheck_text(text_path: Path) -> List[CppcheckIssue]:
"""解析 cppcheck 文本日志(常见行格式:
/path/file.c:111:13: warning: Message [ruleId]
也包含 note:/information:/error: 等等级
"""
content = text_path.read_text(encoding="utf-8", errors="replace")
issues: List[CppcheckIssue] = []
# 常见匹配:路径:行:列: 等级: 消息 [规则]
pattern = re.compile(r"^(?P<file>[^:\n]+?):(?P<line>\d+)(?::\d+)?\:\s*(?P<sev>warning|error|information|note)\:\s*(?P<msg>.*?)(?:\s*\[(?P<id>[^\]]+)\])?\s*$",
re.IGNORECASE)
for raw_line in content.splitlines():
m = pattern.match(raw_line.strip())
if not m:
continue
file_path = Path(m.group("file")).expanduser()
try:
file_path = file_path.resolve()
except Exception:
pass
line_num = int(m.group("line")) if m.group("line") else None
sev = (m.group("sev") or "").lower()
msg = m.group("msg") or ""
rid = m.group("id") or "unknown"
issues.append(
CppcheckIssue(
id=rid,
severity=sev,
message=msg,
locations=[IssueLocation(file_path=file_path, line=line_num)],
)
)
return issues
def read_code_snippet(file_path: Path, center_line: Optional[int], context: int = 30) -> str:
"""读取代码片段"""
try:
lines = file_path.read_text(encoding="utf-8", errors="replace").splitlines()
except Exception:
return ""
if center_line is None:
start = 0
end = min(len(lines), 400)
else:
start = max(0, center_line - 1 - context)
end = min(len(lines), center_line - 1 + context)
snippet = "\n".join(lines[start:end])
return snippet