| 181 | return True |
| 182 | |
| 183 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 184 | violations: list[tuple[int, str]] = [] |
| 185 | |
| 186 | for line_number, line in enumerate(file_content.lines, 1): |
| 187 | stripped = line.strip() |
| 188 | |
| 189 | # Skip line comments |
| 190 | if stripped.startswith("//"): |
| 191 | continue |
| 192 | |
| 193 | # Check 1: direct doctest.h include |
| 194 | if '#include "doctest.h"' in stripped or "#include <doctest.h>" in stripped: |
| 195 | violations.append( |
| 196 | ( |
| 197 | line_number, |
| 198 | 'Use #include "test.h" instead of #include "doctest.h"', |
| 199 | ) |
| 200 | ) |
| 201 | |
| 202 | # Check 2: bare doctest macros |
| 203 | # Two-phase approach for speed: |
| 204 | # Phase 1: fast substring check with small prefix set |
| 205 | if not any(pfx in line for pfx in _FAST_PREFIXES): |
| 206 | continue |
| 207 | |
| 208 | # Phase 2: single combined regex finds which macro matched |
| 209 | for match in _COMBINED_BANNED_RE.finditer(line): |
| 210 | macro_name = match.group(1) |
| 211 | fl_ver = BANNED_MACROS.get(macro_name) |
| 212 | if fl_ver is not None: |
| 213 | violations.append( |
| 214 | (line_number, f"Use {fl_ver}() instead of bare {macro_name}()") |
| 215 | ) |
| 216 | |
| 217 | if violations: |
| 218 | self.violations[file_content.path] = violations |
| 219 | return [] |
| 220 | |
| 221 | |
| 222 | def main() -> None: |