Checker to ensure *.impl.hpp files are only included by *.impl.cpp.hpp files.
| 19 | |
| 20 | |
| 21 | class ImplHppIncludesChecker(FileContentChecker): |
| 22 | """Checker to ensure *.impl.hpp files are only included by *.impl.cpp.hpp files.""" |
| 23 | |
| 24 | def __init__(self) -> None: |
| 25 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 26 | |
| 27 | def should_process_file(self, file_path: str) -> bool: |
| 28 | """Check if file should be processed.""" |
| 29 | # Only check files in src/ and tests/ directories |
| 30 | normalized = file_path.replace("\\", "/") |
| 31 | if "/src/" not in normalized and "/tests/" not in normalized: |
| 32 | return False |
| 33 | |
| 34 | # Check all C++ source and header files |
| 35 | if not file_path.endswith((".cpp", ".h", ".hpp", ".cpp.hpp", ".impl.cpp.hpp")): |
| 36 | return False |
| 37 | |
| 38 | # *.impl.cpp.hpp router files ARE allowed to include *.impl.hpp |
| 39 | if file_path.endswith(".impl.cpp.hpp"): |
| 40 | return False |
| 41 | |
| 42 | return True |
| 43 | |
| 44 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 45 | """Check file content for includes of *.impl.hpp files.""" |
| 46 | violations: list[tuple[int, str]] = [] |
| 47 | in_multiline_comment = False |
| 48 | |
| 49 | # Match #include "path/to/file.impl.hpp" or #include <path/to/file.impl.hpp> |
| 50 | impl_hpp_include_pattern = re.compile( |
| 51 | r'#\s*include\s+[<"]([^>"]+\.impl\.hpp)[>"]' |
| 52 | ) |
| 53 | |
| 54 | for line_number, line in enumerate(file_content.lines, 1): |
| 55 | stripped = line.strip() |
| 56 | |
| 57 | # Track multi-line comment state |
| 58 | if "/*" in line: |
| 59 | in_multiline_comment = True |
| 60 | if "*/" in line: |
| 61 | in_multiline_comment = False |
| 62 | continue |
| 63 | |
| 64 | if in_multiline_comment: |
| 65 | continue |
| 66 | |
| 67 | if stripped.startswith("//"): |
| 68 | continue |
| 69 | |
| 70 | # Check code portion only (before any inline comment) |
| 71 | code_part = line.split("//")[0] |
| 72 | |
| 73 | match = impl_hpp_include_pattern.search(code_part) |
| 74 | if match: |
| 75 | included_file = match.group(1) |
| 76 | violations.append( |
| 77 | ( |
| 78 | line_number, |
no outgoing calls
no test coverage detected