Checks for bare 'using' declarations at file/namespace scope in headers. These are dangerous in unity builds because .cpp.hpp files are concatenated and bare using declarations leak into other translation units.
| 83 | |
| 84 | |
| 85 | class BareUsingChecker(FileContentChecker): |
| 86 | """Checks for bare 'using' declarations at file/namespace scope in headers. |
| 87 | |
| 88 | These are dangerous in unity builds because .cpp.hpp files are concatenated |
| 89 | and bare using declarations leak into other translation units. |
| 90 | """ |
| 91 | |
| 92 | def __init__(self) -> None: |
| 93 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 94 | |
| 95 | def should_process_file(self, file_path: str) -> bool: |
| 96 | """Only process .h, .hpp, .cpp.hpp files under src/fl/.""" |
| 97 | normalized = file_path.replace("\\", "/") |
| 98 | |
| 99 | # Must be under src/fl/ |
| 100 | if "/src/fl/" not in normalized: |
| 101 | return False |
| 102 | |
| 103 | # Skip third_party |
| 104 | if "/third_party/" in normalized: |
| 105 | return False |
| 106 | |
| 107 | # Skip excluded basenames |
| 108 | basename = Path(file_path).name |
| 109 | if basename in _EXCLUDED_BASENAMES: |
| 110 | return False |
| 111 | |
| 112 | # Only header-like files |
| 113 | return normalized.endswith((".h", ".hpp", ".cpp.hpp")) |
| 114 | |
| 115 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 116 | """Check file for bare using declarations at namespace scope.""" |
| 117 | violations: list[tuple[int, str]] = [] |
| 118 | lines = file_content.lines |
| 119 | |
| 120 | # Track scope: stack of scope types |
| 121 | # 'namespace' = file-level or named namespace (flagged) |
| 122 | # 'local' = class/struct/function/anon-namespace (not flagged) |
| 123 | scope_stack: list[str] = [] |
| 124 | in_block_comment = False |
| 125 | |
| 126 | for line_num, line in enumerate(lines, 1): |
| 127 | # Handle block comments |
| 128 | if in_block_comment: |
| 129 | if "*/" in line: |
| 130 | in_block_comment = False |
| 131 | continue |
| 132 | |
| 133 | # Check for block comment start, but only OUTSIDE single-line comments |
| 134 | # Strip the // comment portion first to avoid false positives |
| 135 | # e.g. "// implementations in viz/*.cpp.hpp" should not trigger |
| 136 | line_before_line_comment = line |
| 137 | line_comment_pos = line.find("//") |
| 138 | if line_comment_pos >= 0: |
| 139 | line_before_line_comment = line[:line_comment_pos] |
| 140 | |
| 141 | if "/*" in line_before_line_comment: |
| 142 | start_pos = line_before_line_comment.index("/*") |
no outgoing calls