================
@@ -0,0 +1,394 @@
+#!/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 Dict, List, Optional, Sequence, Tuple, Union, overload
+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")
+
+
+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 key_for(line: str):
+        m = DOC_LINE_RE.match(line)
+        if not m:
+            return (1, "")
+        return (0, m.group("label"))
+
+    while i < n:
+        line = lines[i]
+        if line.lstrip().startswith(".. csv-table::"):
+            out.append(line)
+            i += 1
+
+            while i < n and (lines[i].startswith(" ") or lines[i].strip() == 
""):
+                if DOC_LINE_RE.match(lines[i]):
+                    break
+                out.append(lines[i])
+                i += 1
+
+            entries: List[str] = []
+            while i < n and lines[i].startswith(" "):
+                entries.append(lines[i])
+                i += 1
+
+            entries_sorted = sorted(entries, key=key_for)
+            out.extend(entries_sorted)
+            continue
+
+        out.append(line)
+        i += 1
+
+    return out
+
+
+@overload
+def normalize_list_rst(data: str) -> str:
+    ...
+
+
+@overload
+def normalize_list_rst(data: List[str]) -> List[str]:
+    ...
+
+
+def normalize_list_rst(data: Union[str, List[str]]) -> Union[str, List[str]]:
+    """Normalize list.rst; returns same type as input (str or list).
+
+    - If given a string, returns a single normalized string.
+    - If given a sequence of lines, returns a list of lines.
+    """
+    if isinstance(data, str):
+        lines = data.splitlines(True)
+        return "".join(_normalize_list_rst_lines(lines))
+    else:
+        return _normalize_list_rst_lines(data)
+
+
+def find_heading(lines: Sequence[str], title: str) -> Optional[int]:
+    """Find heading start index for a section underlined with ^ characters.
+
+    The function looks for a line equal to `title` followed by a line that
+    consists solely of ^, which matches the ReleaseNotes style for subsection
+    headings used here.
+
+    Returns index of the title line, or None if not found.
+    """
+    for i in range(len(lines) - 1):
+        if lines[i].rstrip("\n") == title:
+            underline = lines[i + 1].rstrip("\n")
+            if underline and set(underline) == {"^"} and len(underline) >= 
len(title):
+                return i
+    return None
+
+
+def extract_label(text: str) -> str:
+    m = DOC_LABEL_RN_RE.search(text)
+    return m.group("label") if m else text
+
+
+def is_bullet_start(line: str) -> bool:
+    return line.startswith("- ")
+
+
+def parse_bullet_blocks(
----------------
vbvictor wrote:

If it is "private" function, start with `_`

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