| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- #!/usr/bin/env python3
- """Checks for missing or incorrect header guards."""
- __copyright__ = """
- Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- Exceptions. See /LICENSE for license information.
- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- """
- from pathlib import Path
- import re
- import sys
- from typing import Iterable, List, NamedTuple, Optional
- class Guard(NamedTuple):
- """A guard line in a file."""
- line: int
- guard: str
- def find_guard(
- lines: List[str], pattern: str, from_end: bool
- ) -> Optional[Guard]:
- """Searches the lines for something matching the pattern."""
- lines_range: Iterable[str] = lines
- if from_end:
- lines_range = reversed(lines)
- for index, line in enumerate(lines_range):
- m = re.match(pattern, line)
- if m:
- if from_end:
- index = len(lines) - index - 1
- return Guard(index, m[1])
- return None
- def maybe_replace(
- lines: List[str], old_guard: Guard, guard_prefix: str, guard: str
- ) -> None:
- """Replaces a header guard in the file if needed."""
- if guard != old_guard.guard:
- line = lines[old_guard.line].rstrip("\n")
- print(f"- Replacing line {old_guard.line}: `{line}`", file=sys.stderr)
- lines[old_guard.line] = f"{guard_prefix} {guard}\n"
- def check_path(path: Path) -> bool:
- """Checks the path for header guard issues."""
- if path.suffix != ".h":
- print(f"Not a header: {path}", file=sys.stderr)
- return True
- with path.open() as f:
- lines = f.readlines()
- guard = str(path).upper().replace("/", "_").replace(".", "_") + "_"
- ifndef = find_guard(lines, "#ifndef ([A-Z_]+_H_)", False)
- define = find_guard(lines, "#define ([A-Z_]+_H_)", False)
- endif = find_guard(lines, "#endif(?: // ([A-Z_]+_H_))?", True)
- if ifndef is None or define is None or endif is None:
- print(f"Incomplete header guard in {path}:", file=sys.stderr)
- if ifndef is None:
- print(f"- Missing `#ifndef {guard}`", file=sys.stderr)
- if define is None:
- print(f"- Missing `#define {guard}`", file=sys.stderr)
- if endif is None:
- print(f"- Missing `#endif // {guard}`", file=sys.stderr)
- return True
- if ifndef.line + 1 != define.line:
- print(
- f"Non-consecutive header guard in {path}: "
- f"#ifndef on line {ifndef.line + 1}, "
- f"#define on line {define.line + 1}.",
- file=sys.stderr,
- )
- return True
- if endif.line != len(lines) - 1:
- print(
- f"Misordered header guard in {path}: #endif on line {endif.line}, "
- f"should be on last line ({len(lines) - 1}).",
- file=sys.stderr,
- )
- return True
- if guard != ifndef.guard or guard != define.guard or guard != endif.guard:
- print(f"Fixing header guard in {path} to {guard}:", file=sys.stderr)
- maybe_replace(lines, ifndef, "#ifndef", guard)
- maybe_replace(lines, define, "#define", guard)
- maybe_replace(lines, endif, "#endif //", guard)
- with path.open("w") as f:
- f.writelines(lines)
- return True
- return False
- def main() -> None:
- has_errors = False
- for arg in sys.argv[1:]:
- if check_path(Path(arg)):
- has_errors = True
- if has_errors:
- exit(1)
- if __name__ == "__main__":
- main()
|