Skip to content

Commit 29c0406

Browse files
committed
Enhance --trim-path-prefix to also trim paths in report messages
1 parent 4467991 commit 29c0406

File tree

3 files changed

+330
-0
lines changed

3 files changed

+330
-0
lines changed

tools/report-converter/codechecker_report_converter/report/__init__.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
import json
1414
import logging
1515
import os
16+
import re
1617

18+
from codechecker_report_converter.util import trim_path_prefixes
1719
from typing import Callable, Dict, List, Optional, Protocol, Set, Tuple
1820

1921
from .. import util
@@ -395,6 +397,45 @@ def trim_path_prefixes(self, path_prefixes: Optional[List[str]] = None):
395397
):
396398
event.file.trim(path_prefixes)
397399

400+
# Also trim file paths in the content of Reports
401+
if path_prefixes:
402+
self.message = self._trim_path_in_text(self.message, path_prefixes)
403+
404+
if self.static_message:
405+
self.static_message = self._trim_path_in_text(
406+
self.static_message, path_prefixes
407+
)
408+
409+
for event in self.bug_path_events:
410+
event.message = self._trim_path_in_text(
411+
event.message, path_prefixes
412+
)
413+
414+
for note in self.notes:
415+
note.message = self._trim_path_in_text(
416+
note.message, path_prefixes
417+
)
418+
419+
for macro in self.macro_expansions:
420+
macro.message = self._trim_path_in_text(
421+
macro.message, path_prefixes
422+
)
423+
424+
def _trim_path_in_text(self, text: str, path_prefixes: List[str]) -> str:
425+
"""
426+
Finds file paths in text and trims their prefixes using the same logic
427+
as trim_path_prefixes from util.py.
428+
"""
429+
if not path_prefixes:
430+
return text
431+
432+
result = text
433+
for path in util.find_paths_in_text(text):
434+
trimmed_path = util.trim_path_prefixes(path, path_prefixes)
435+
result = result.replace(path, trimmed_path)
436+
437+
return result
438+
398439
@property
399440
def files(self) -> Set[File]:
400441
""" Returns all referenced file paths. """

tools/report-converter/codechecker_report_converter/util.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@
1818

1919
LOG = logging.getLogger('report-converter')
2020

21+
# Regular expression patterns for path matching
22+
DRIVE_LETTER = r'[A-Za-z]:'
23+
PATH_SEPARATOR = r'[/\\]'
24+
PATH_COMPONENT = r'[^/\s\\]+'
25+
FILE_EXTENSION = r'\.[a-zA-Z0-9]+'
26+
PATH_PATTERN = f'(?:{DRIVE_LETTER})?{PATH_SEPARATOR}{PATH_COMPONENT}' \
27+
f'(?:{PATH_SEPARATOR}{PATH_COMPONENT})*(?:{FILE_EXTENSION})?'
28+
29+
30+
def find_paths_in_text(text: str) -> List[str]:
31+
"""
32+
Find all potential file paths in the given text.
33+
Handles both Unix and Windows paths.
34+
"""
35+
return [match.group() for match in re.finditer(PATH_PATTERN, text)]
36+
2137

2238
def get_last_mod_time(file_path: str) -> Optional[float]:
2339
""" Return the last modification time of a file. """

tools/report-converter/tests/unit/util/test_trim_path_prefix.py

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
import unittest
1313

1414
from codechecker_report_converter.util import trim_path_prefixes
15+
from codechecker_report_converter.report import (
16+
BugPathEvent,
17+
File,
18+
MacroExpansion,
19+
Report,
20+
)
1521

1622

1723
class TrimPathPrefixTestCase(unittest.TestCase):
@@ -70,3 +76,270 @@ def test_prefix_blob(self):
7076
self.assertEqual('my_proj/x.cpp',
7177
trim_path_prefixes('/home/jsmith/my_proj/x.cpp',
7278
['/home/jsmith/']))
79+
80+
def test_remove_only_root_prefix(self):
81+
"""Test removing only the root '/' prefix from paths."""
82+
83+
test_paths = ["/a/b/c", "/foo.txt", "/dir/subdir/file.cpp", "/"]
84+
85+
expected_paths = ["a/b/c", "foo.txt", "dir/subdir/file.cpp", ""]
86+
87+
for test_path, expected in zip(test_paths, expected_paths):
88+
self.assertEqual(
89+
expected,
90+
trim_path_prefixes(test_path, ["/"]),
91+
f"Failed to remove root prefix from {test_path}",
92+
)
93+
94+
def test_trim_path_in_message(self):
95+
"""
96+
Test trimming path prefixes in report messages and bug path events.
97+
"""
98+
99+
test_file = File("/path/to/workspace/src/example.cpp")
100+
101+
test_cases = [
102+
# Simple message with one path
103+
{
104+
"name": "simple_message",
105+
"message": ("Error in file "
106+
"/path/to/workspace/src/example.cpp:10:20"),
107+
"bug_path_events": [
108+
BugPathEvent(
109+
"Found issue at "
110+
"/path/to/workspace/include/header.h:5:10",
111+
File("/path/to/workspace/include/header.h"),
112+
5,
113+
10,
114+
)
115+
],
116+
"expected_message": "Error in file src/example.cpp:10:20",
117+
"expected_bug_path_messages": [
118+
"Found issue at include/header.h:5:10"
119+
],
120+
},
121+
# Complex message with multiple paths
122+
{
123+
"name": "complex_message",
124+
"message": (
125+
"Multiple errors: "
126+
"/path/to/workspace/src/example.cpp:10:20 and "
127+
"/path/to/workspace/include/header.h:5:10"
128+
),
129+
"bug_path_events": [],
130+
"expected_message": (
131+
"Multiple errors: src/example.cpp:10:20 and "
132+
"include/header.h:5:10"
133+
),
134+
"expected_bug_path_messages": [],
135+
},
136+
]
137+
138+
for case in test_cases:
139+
report = Report(
140+
file=test_file,
141+
line=10,
142+
column=20,
143+
message=case["message"],
144+
checker_name="test-checker",
145+
severity="HIGH",
146+
)
147+
148+
for event in case["bug_path_events"]:
149+
report.bug_path_events.append(event)
150+
151+
report.trim_path_prefixes(["/path/to/workspace"])
152+
153+
self.assertEqual(
154+
case["expected_message"],
155+
report.message,
156+
f"Failed to trim message in {case['name']} case",
157+
)
158+
159+
for i, expected_msg in enumerate(
160+
case["expected_bug_path_messages"]
161+
):
162+
# Note: Bug path events are 1-indexed in the report
163+
self.assertEqual(
164+
expected_msg,
165+
report.bug_path_events[i + 1].message,
166+
"Failed to trim bug path event message "
167+
"in {case['name']} case",
168+
)
169+
170+
def test_trim_path_in_complex_message(self):
171+
172+
test_file = File("/path/to/workspace/src/example.cpp")
173+
174+
message = (
175+
"Multiple errors: "
176+
"/path/to/workspace/src/example.cpp:10:20 and "
177+
"/path/to/workspace/include/header.h:5:10"
178+
)
179+
180+
report = Report(
181+
file=test_file,
182+
line=10,
183+
column=20,
184+
message=message,
185+
checker_name="test-checker",
186+
severity="HIGH",
187+
)
188+
189+
report.trim_path_prefixes(["/path/to/workspace"])
190+
191+
expected = (
192+
"Multiple errors: src/example.cpp:10:20 and "
193+
"include/header.h:5:10"
194+
)
195+
self.assertEqual(expected, report.message)
196+
197+
def test_trim_path_in_macro_expansions(self):
198+
"""
199+
Test trimming path prefixes in macro expansions with various scenarios.
200+
"""
201+
202+
test_file = File("/path/to/workspace/src/example.cpp")
203+
204+
test_cases = [
205+
# Single macro with simple path
206+
{
207+
"name": "single_macro",
208+
"files": [File("/path/to/workspace/include/macro.h")],
209+
"messages": [
210+
"Macro expanded from "
211+
"/path/to/workspace/include/macro.h:5:10"
212+
],
213+
"names": ["TEST_MACRO"],
214+
"lines": [5],
215+
"columns": [10],
216+
"expected_messages": [
217+
"Macro expanded from include/macro.h:5:10"
218+
],
219+
"expected_paths": ["include/macro.h"],
220+
},
221+
# Multiple macros
222+
{
223+
"name": "multiple_macros",
224+
"files": [
225+
File("/path/to/workspace/include/macro1.h"),
226+
File("/path/to/workspace/include/macro2.h"),
227+
],
228+
"messages": [
229+
"First macro from "
230+
"/path/to/workspace/include/macro1.h:5:10",
231+
"Second macro from "
232+
"/path/to/workspace/include/macro2.h:15:20",
233+
],
234+
"names": ["MACRO1", "MACRO2"],
235+
"lines": [5, 15],
236+
"columns": [10, 20],
237+
"expected_messages": [
238+
"First macro from include/macro1.h:5:10",
239+
"Second macro from include/macro2.h:15:20",
240+
],
241+
"expected_paths": ["include/macro1.h", "include/macro2.h"],
242+
},
243+
# Macro with multiple path references
244+
{
245+
"name": "multiple_paths",
246+
"files": [File("/path/to/workspace/include/macro.h")],
247+
"messages": [
248+
"Macro expanded from "
249+
"/path/to/workspace/include/macro.h:5:10 "
250+
"includes /path/to/workspace/include/header.h:3:4"
251+
],
252+
"names": ["TEST_MACRO"],
253+
"lines": [5],
254+
"columns": [10],
255+
"expected_messages": [
256+
"Macro expanded from include/macro.h:5:10 "
257+
"includes include/header.h:3:4"
258+
],
259+
"expected_paths": ["include/macro.h"],
260+
},
261+
]
262+
263+
for case in test_cases:
264+
macros = []
265+
for i in range(len(case["files"])):
266+
macros.append(
267+
MacroExpansion(
268+
message=case["messages"][i],
269+
name=case["names"][i],
270+
file=case["files"][i],
271+
line=case["lines"][i],
272+
column=case["columns"][i],
273+
)
274+
)
275+
276+
report = Report(
277+
file=test_file,
278+
line=10,
279+
column=20,
280+
message=f"Error in macro expansion - {case['name']}",
281+
checker_name="test-checker",
282+
severity="HIGH",
283+
macro_expansions=macros,
284+
)
285+
286+
report.trim_path_prefixes(["/path/to/workspace"])
287+
288+
for i, (expected_msg, expected_path) in enumerate(
289+
zip(case["expected_messages"], case["expected_paths"])
290+
):
291+
self.assertEqual(
292+
expected_msg,
293+
report.macro_expansions[i].message,
294+
f"Failed to trim message in {case['name']} case",
295+
)
296+
self.assertEqual(
297+
expected_path,
298+
report.macro_expansions[i].file.path,
299+
f"Failed to trim file path in {case['name']} case",
300+
)
301+
302+
def test_macro_expansion_edge_cases(self):
303+
"""Test trimming path prefixes in macro expansions with edge cases."""
304+
305+
test_file = File("/path/to/workspace/src/example.cpp")
306+
307+
edge_cases = [
308+
(File(""), "Empty file path"),
309+
(File("/"), "Root path only"),
310+
(File("/path/to/workspace/"), "Directory path ending with slash"),
311+
(File("relative/path.h"), "Relative path"),
312+
]
313+
314+
for file, desc in edge_cases:
315+
macro = MacroExpansion(
316+
message=f"Macro with {desc}: {file.path}",
317+
name="TEST_MACRO",
318+
file=file,
319+
line=1,
320+
column=1,
321+
)
322+
323+
report = Report(
324+
file=test_file,
325+
line=10,
326+
column=20,
327+
message=f"Error in macro expansion - {desc}",
328+
checker_name="test-checker",
329+
severity="HIGH",
330+
macro_expansions=[macro],
331+
)
332+
333+
report.trim_path_prefixes(["/path/to/workspace"])
334+
335+
if desc in ["Empty file path", "Root path only"]:
336+
self.assertEqual(
337+
file.path, report.macro_expansions[0].file.path
338+
)
339+
elif desc == "Directory path ending with slash":
340+
self.assertEqual("", report.macro_expansions[0].file.path)
341+
else:
342+
# Relative path unchanged
343+
self.assertEqual(
344+
file.path, report.macro_expansions[0].file.path
345+
)

0 commit comments

Comments
 (0)