Checker class for banned headers.
| 436 | |
| 437 | |
| 438 | class BannedHeadersChecker(FileContentChecker): |
| 439 | """Checker class for banned headers.""" |
| 440 | |
| 441 | def __init__(self, banned_headers_list: list[str], strict_mode: bool = False): |
| 442 | """Initialize with the list of banned headers to check for. |
| 443 | |
| 444 | Args: |
| 445 | banned_headers_list: List of headers to ban |
| 446 | strict_mode: If True, do not allow "// ok include" bypass (for fl/ directory) |
| 447 | """ |
| 448 | self.banned_headers_list = banned_headers_list |
| 449 | self.strict_mode = strict_mode |
| 450 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 451 | |
| 452 | # Pre-compile a single regex that matches any banned header in an #include. |
| 453 | # This replaces the O(H) per-line string search with a single O(1) regex match, |
| 454 | # where H is the number of banned headers (~35). |
| 455 | if banned_headers_list: |
| 456 | escaped = [re.escape(h) for h in banned_headers_list] |
| 457 | pattern = r'#include\s+[<"](' + "|".join(escaped) + r')[>"]' |
| 458 | self._banned_header_regex: re.Pattern[str] | None = re.compile(pattern) |
| 459 | else: |
| 460 | self._banned_header_regex = None |
| 461 | |
| 462 | def should_process_file(self, file_path: str) -> bool: |
| 463 | """Check if file should be processed for banned headers.""" |
| 464 | # Check file extension |
| 465 | if not file_path.endswith((".cpp", ".h", ".hpp", ".cpp.hpp", ".ino")): |
| 466 | return False |
| 467 | |
| 468 | # Check if file is in excluded list (handles both Windows and Unix paths) |
| 469 | if is_excluded_file(file_path): |
| 470 | return False |
| 471 | |
| 472 | return True |
| 473 | |
| 474 | @classmethod |
| 475 | def _matches_pattern(cls, file_path: str, pattern: str) -> bool: |
| 476 | r"""Check if file path matches the pattern. |
| 477 | |
| 478 | Since all exception patterns are now exact file paths (no wildcards), |
| 479 | this performs a simple exact match after normalizing path separators. |
| 480 | |
| 481 | Args: |
| 482 | file_path: The file path to check (may use \ or / separators) |
| 483 | pattern: The pattern to match (uses / separators) |
| 484 | |
| 485 | Returns: |
| 486 | True if the file path matches the pattern |
| 487 | """ |
| 488 | normalized_path = file_path.replace("\\", "/") |
| 489 | |
| 490 | # Exact match: pattern equals the full path or just the suffix |
| 491 | # Examples: |
| 492 | # pattern="fl/stl/mutex.h" matches "src/fl/stl/mutex.h" |
| 493 | # pattern="inplacenew.h" matches "src/inplacenew.h" |
| 494 | return pattern == normalized_path or normalized_path.endswith("/" + pattern) |
| 495 |