Check that includes within each section of _build.cpp.hpp are alphabetically sorted. Rules: - Same-level *.cpp.hpp includes must be sorted alphabetically. - Subdirectory _build.cpp.hpp includes must be sorted alphabetically. - .h header includes at the top (before sections) are
(src_dir: Path)
| 537 | |
| 538 | |
| 539 | def _check_alphabetical_order(src_dir: Path) -> list[str]: |
| 540 | """ |
| 541 | Check that includes within each section of _build.cpp.hpp are alphabetically sorted. |
| 542 | |
| 543 | Rules: |
| 544 | - Same-level *.cpp.hpp includes must be sorted alphabetically. |
| 545 | - Subdirectory _build.cpp.hpp includes must be sorted alphabetically. |
| 546 | - .h header includes at the top (before sections) are exempt from sorting. |
| 547 | |
| 548 | Returns: |
| 549 | list of violation strings |
| 550 | """ |
| 551 | violations: list[str] = [] |
| 552 | |
| 553 | include_pattern = re.compile(r'^\s*#include\s+"([^"]+\.cpp\.hpp)"', re.MULTILINE) |
| 554 | |
| 555 | for build_hpp in src_dir.rglob(BUILD_HPP): |
| 556 | content = build_hpp.read_text(encoding="utf-8") |
| 557 | rel_file = build_hpp.relative_to(PROJECT_ROOT).as_posix() |
| 558 | |
| 559 | includes = list(include_pattern.finditer(content)) |
| 560 | if len(includes) < 2: |
| 561 | continue |
| 562 | |
| 563 | # Split into sections |
| 564 | same_level: list[tuple[str, int]] = [] |
| 565 | subdir: list[tuple[str, int]] = [] |
| 566 | for match in includes: |
| 567 | path = match.group(1) |
| 568 | line_num = _get_line_number(content, match.start()) |
| 569 | if path.endswith(BUILD_HPP): |
| 570 | subdir.append((path, line_num)) |
| 571 | else: |
| 572 | same_level.append((path, line_num)) |
| 573 | |
| 574 | # Check each section is alphabetically sorted |
| 575 | for section_name, section in [ |
| 576 | ("current directory", same_level), |
| 577 | ("sub directory", subdir), |
| 578 | ]: |
| 579 | if len(section) < 2: |
| 580 | continue |
| 581 | paths = [p for p, _ in section] |
| 582 | sorted_paths = sorted(paths) |
| 583 | if paths != sorted_paths: |
| 584 | # Find first out-of-order pair |
| 585 | for i in range(1, len(paths)): |
| 586 | if paths[i] < paths[i - 1]: |
| 587 | line_num = section[i][1] |
| 588 | violations.append( |
| 589 | f"{rel_file}:{line_num}: {section_name} includes not alphabetically sorted. " |
| 590 | f"'{paths[i]}' comes before '{paths[i - 1]}' but should come after it." |
| 591 | ) |
| 592 | break |
| 593 | |
| 594 | return violations |
| 595 | |
| 596 |
no test coverage detected