""" 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 只有一层 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[^:\n]+?):(?P\d+)(?::\d+)?\:\s*(?Pwarning|error|information|note)\:\s*(?P.*?)(?:\s*\[(?P[^\]]+)\])?\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