Check file for bare using declarations at namespace scope.
(self, file_content: FileContent)
| 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("/*") |
| 143 | rest = line[start_pos + 2 :] |
| 144 | if "*/" not in rest: |
| 145 | in_block_comment = True |
| 146 | continue # Skip lines that start/contain block comments |
| 147 | |
| 148 | # Skip preprocessor directives |
| 149 | if _RE_PREPROCESSOR.match(line): |
| 150 | continue |
| 151 | |
| 152 | # Strip comments for analysis |
| 153 | clean = _strip_strings_and_comments(line) |
| 154 | |
| 155 | # Track braces for scope depth |
| 156 | # Count opens and closes in the clean line |
| 157 | open_braces = clean.count("{") |
| 158 | close_braces = clean.count("}") |
| 159 | |
| 160 | # Before processing braces, check if this line opens a scope |
| 161 | # Determine what kind of scope is being opened |
| 162 | scope_types_to_push: list[str] = [] |
| 163 | |
| 164 | if open_braces > 0: |
| 165 | # Determine the type of scope being opened |
| 166 | if _RE_ANON_NAMESPACE.match(clean): |
| 167 | # Anonymous namespace — local scope (safe zone) |
| 168 | scope_types_to_push.append("local") |
| 169 | open_braces -= 1 # consumed one brace |
| 170 | elif _RE_NAMED_NAMESPACE.match(clean): |
| 171 | # Named namespace — still namespace scope (flagged) |
| 172 | scope_types_to_push.append("namespace") |