Check if a C++ file has #include directives after namespace declarations. Uses a fast check first, then falls back to thorough check if needed. Args: file_path (Path): Path to the C++ file to check Returns: List[IncludeViolation]: List of violations found
(
file_path: Path,
)
| 181 | |
| 182 | |
| 183 | def find_includes_after_namespace( |
| 184 | file_path: Path, |
| 185 | ) -> list[IncludeViolation]: |
| 186 | """ |
| 187 | Check if a C++ file has #include directives after namespace declarations. |
| 188 | Uses a fast check first, then falls back to thorough check if needed. |
| 189 | |
| 190 | Args: |
| 191 | file_path (Path): Path to the C++ file to check |
| 192 | |
| 193 | Returns: |
| 194 | List[IncludeViolation]: List of violations found |
| 195 | """ |
| 196 | try: |
| 197 | with open(file_path, "r", encoding="utf-8") as f: |
| 198 | content = f.readlines() |
| 199 | |
| 200 | violations: list[IncludeViolation] = [] |
| 201 | current_namespace: NamespaceInfo | None = None |
| 202 | seen_namespace = False # Track if we've ever seen a namespace |
| 203 | seen_include_after_namespace = ( |
| 204 | False # Track if include appears after any namespace |
| 205 | ) |
| 206 | |
| 207 | # Compiled regex patterns for validation (only used after fast string search) |
| 208 | using_namespace_pattern = re.compile(r"^\s*using\s+namespace\s+\w+\s*;") |
| 209 | namespace_open_pattern = re.compile(r"^\s*namespace\s+\w+\s*\{") |
| 210 | include_pattern = re.compile(r"^\s*#\s*include") |
| 211 | |
| 212 | def truncate_snippet(text: str, max_len: int = 50) -> str: |
| 213 | """Truncate text to max_len characters.""" |
| 214 | text = text.strip() |
| 215 | if len(text) <= max_len: |
| 216 | return text |
| 217 | return text[: max_len - 3] + "..." |
| 218 | |
| 219 | for i, line in enumerate(content, 1): |
| 220 | original_line = line |
| 221 | line = line.strip() |
| 222 | |
| 223 | # Skip empty lines and comments |
| 224 | if not line or line.startswith("//") or line.startswith("/*"): |
| 225 | continue |
| 226 | |
| 227 | # Check for closing namespace brace - reset namespace tracking |
| 228 | if current_namespace and line.startswith("}"): |
| 229 | current_namespace = None |
| 230 | |
| 231 | # Fast string search for "using namespace" before regex validation |
| 232 | if "using namespace" in line: |
| 233 | if using_namespace_pattern.match(line): |
| 234 | current_namespace = NamespaceInfo( |
| 235 | line_number=i, snippet=truncate_snippet(original_line) |
| 236 | ) |
| 237 | seen_namespace = True |
| 238 | |
| 239 | # Fast string search for "namespace X {" before regex validation |
| 240 | if "namespace" in line and "{" in line: |
no test coverage detected