================
@@ -0,0 +1,402 @@
+#!/usr/bin/env python3
+#
+# 
===-----------------------------------------------------------------------===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+# 
===-----------------------------------------------------------------------===#
+
+"""
+
+Clang-Tidy Alphabetical Order Checker
+=====================================
+
+Normalize Clang-Tidy documentation with deterministic sorting for 
linting/tests.
+
+Behavior:
+- Sort entries in docs/clang-tidy/checks/list.rst csv-table.
+- Sort key sections in docs/ReleaseNotes.rst.
+- Detect duplicated entries in 'Changes in existing checks'.
+
+Flags:
+  -o/--output  Write normalized content to this path instead of updating docs.
+"""
+
+import argparse
+import io
+import os
+import re
+import sys
+from typing import List, Optional, Sequence, Tuple, NamedTuple, DefaultDict
+from collections import defaultdict
+from operator import itemgetter
+
+# Matches a :doc:`label <path>` or :doc:`label` reference anywhere in text and
+# captures the label. Used to sort bullet items alphabetically in ReleaseNotes
+# items by their label.
+DOC_LABEL_RN_RE = re.compile(r":doc:`(?P<label>[^`<]+)\s*(?:<[^>]+>)?`")
+
+# Matches a single csv-table row line in list.rst that begins with a :doc:
+# reference, capturing the label. Used to extract the sort key per row.
+DOC_LINE_RE = re.compile(r"^\s*:doc:`(?P<label>[^`<]+?)\s*<[^>]+>`.*$")
+
+
+EXTRA_DIR = os.path.join(os.path.dirname(__file__), "../..")
+DOCS_DIR = os.path.join(EXTRA_DIR, "docs")
+CLANG_TIDY_DOCS_DIR = os.path.join(DOCS_DIR, "clang-tidy")
+CHECKS_DOCS_DIR = os.path.join(CLANG_TIDY_DOCS_DIR, "checks")
+LIST_DOC = os.path.join(CHECKS_DOCS_DIR, "list.rst")
+RELEASE_NOTES_DOC = os.path.join(DOCS_DIR, "ReleaseNotes.rst")
+
+
+# Label extracted from :doc:`...`.
+CheckLabel = str
+Lines = List[str]
+BulletBlock = List[str]
+
+# Pair of the extracted label and its block
+BulletItem = Tuple[CheckLabel, BulletBlock]
+
+# Index of the first line of a bullet block within the full lines list.
+BulletStart = int
+
+# All occurrences for a given label.
+DuplicateOccurrences = List[Tuple[BulletStart, BulletBlock]]
+
+
+class BulletBlocks(NamedTuple):
+    """Structured result of parsing a bullet-list section.
+
+    - prefix: lines before the first bullet within the section range.
+    - blocks: list of (label, block-lines) pairs for each bullet block.
+    - suffix: lines after the last bullet within the section range.
+    """
+
+    prefix: Lines
+    blocks: List[BulletItem]
+    suffix: Lines
+
+
+class ScannedBlocks(NamedTuple):
+    """Result of scanning bullet blocks within a section range.
+
+    - blocks_with_pos: list of (start_index, block_lines) for each bullet 
block.
+    - next_index: index where scanning stopped; start of the suffix region.
+    """
+
+    blocks_with_pos: List[Tuple[BulletStart, BulletBlock]]
+    next_index: int
+
+
+def _scan_bullet_blocks(lines: Sequence[str], start: int, end: int) -> 
ScannedBlocks:
+    """Scan consecutive bullet blocks and return (blocks_with_pos, next_index).
+
+    Each entry in blocks_with_pos is a tuple of (start_index, block_lines).
+    next_index is the index where scanning stopped (start of suffix).
+    """
+    i = start
+    n = end
+    blocks_with_pos: List[Tuple[BulletStart, BulletBlock]] = []
+    while i < n:
+        if not _is_bullet_start(lines[i]):
+            break
+        bstart = i
+        i += 1
+        while i < n and not _is_bullet_start(lines[i]):
+            if (
+                i + 1 < n
+                and set(lines[i + 1].rstrip("\n")) == {"^"}
+                and lines[i].strip()
+            ):
+                break
+            i += 1
+        block: BulletBlock = list(lines[bstart:i])
+        blocks_with_pos.append((bstart, block))
+    return ScannedBlocks(blocks_with_pos, i)
+
+
+def read_text(path: str) -> List[str]:
+    with io.open(path, "r", encoding="utf-8") as f:
+        return f.read().splitlines(True)
+
+
+def write_text(path: str, content: str) -> None:
+    with io.open(path, "w", encoding="utf-8", newline="") as f:
+        f.write(content)
+
+
+def _normalize_list_rst_lines(lines: Sequence[str]) -> List[str]:
+    """Return normalized content of checks list.rst as a list of lines."""
+    out: List[str] = []
+    i = 0
+    n = len(lines)
+
+    def check_name(line: str):
----------------
EugeneZelenko wrote:

Missing return type.

https://github.com/llvm/llvm-project/pull/166072
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to