Skip to content

Commit 543e80f

Browse files
karthiknadigCopilot
andcommitted
fix: avoid coverage scanner suffix copies (Refs #534)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 31ab117 commit 543e80f

2 files changed

Lines changed: 26 additions & 6 deletions

File tree

‎scripts/coverage_detail.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515

1616
from quality_snapshot import SnapshotError, parse_lcov
1717

18+
RAW_STRING_START = re.compile(r'(?:br|cr|r)(#*)"')
19+
CHARACTER_LITERAL = re.compile(r"'(?:[^'\\\n]|\\(?:u\{[0-9a-fA-F_]+\}|x[0-9a-fA-F]{2}|.))'")
20+
ITEM_ATTRIBUTE = re.compile(r'\s*#\s*\[')
21+
1822

1923
def run(*args: str, cwd: Path) -> str:
2024
return subprocess.check_output(args, cwd=cwd, text=True, encoding='utf-8')
@@ -112,10 +116,10 @@ def code_mask(source: str) -> str:
112116
if depth:
113117
raise SnapshotError('Unterminated Rust block comment')
114118
else:
115-
raw = re.match(r'(?:br|cr|r)(#*)"', source[i:])
119+
raw = RAW_STRING_START.match(source, i)
116120
if raw:
117121
close = '"' + raw[1]
118-
end = source.find(close, i + raw.end())
122+
end = source.find(close, raw.end())
119123
if end == -1:
120124
raise SnapshotError('Unterminated Rust raw string')
121125
end += len(close)
@@ -132,9 +136,9 @@ def code_mask(source: str) -> str:
132136
else:
133137
raise SnapshotError('Unterminated Rust string')
134138
elif source[i] == "'":
135-
char = re.match(r"'(?:[^'\\\n]|\\(?:u\{[0-9a-fA-F_]+\}|x[0-9a-fA-F]{2}|.))'", source[i:])
139+
char = CHARACTER_LITERAL.match(source, i)
136140
if char:
137-
end = i + char.end()
141+
end = char.end()
138142
if end > i:
139143
for n in range(i, end):
140144
if chars[n] != '\n':
@@ -153,10 +157,10 @@ def test_lines(source: str) -> set[int]:
153157
end = match.end()
154158
# Other attributes belong to this same item, not its body.
155159
while True:
156-
attr = re.match(r'\s*#\s*\[', masked[end:])
160+
attr = ITEM_ATTRIBUTE.match(masked, end)
157161
if not attr:
158162
break
159-
end += attr.end()
163+
end = attr.end()
160164
depth = 1
161165
while depth and end < len(masked):
162166
depth += (masked[end] == '[') - (masked[end] == ']')

‎scripts/tests/test_coverage_detail.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,22 @@ def test_literals_nested_comments_and_attributes_do_not_move_boundaries(self):
137137
self.assertEqual(detail.test_lines(source), set(range(3, 10)))
138138
self.assertEqual(detail.code_mask(source).count('\n'), source.count('\n'))
139139

140+
def test_literal_scanning_matches_at_offsets_without_copying_source_suffixes(self):
141+
class NoSlices(str):
142+
def __getitem__(self, key):
143+
if isinstance(key, slice):
144+
raise AssertionError('scanner copied a source suffix')
145+
return super().__getitem__(key)
146+
147+
prefix = "fn real<'a>(x: &'a str) { "
148+
literals = ['r"text"', 'br##"}\\n{"##', 'cr#"text"#',
149+
"'{'", r"'\u{7b}'", r"'\x7b'", r"'\''"]
150+
source = prefix + '; '.join(literals) + '; }\n'
151+
expected = prefix + '; '.join(' ' * len(value) for value in literals) + '; }\n'
152+
for copies in [1, 1000]:
153+
with self.subTest(copies=copies):
154+
self.assertEqual(detail.code_mask(NoSlices(source * copies)), expected * copies)
155+
140156
def test_cfg_test_function_and_external_module_are_test_code(self):
141157
source = '#[cfg(test)]\nfn helper() {}\n#[cfg(test)]\nmod fixtures;\nfn real() {}\n'
142158
self.assertEqual(detail.test_lines(source), {1, 2, 3, 4})

0 commit comments

Comments
 (0)