[clang-tools-extra] [clang-tidy][NFC] Fix list.rst and add_new_check.py (PR #192228)
Zeyi Xu via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 06:15:55 PDT 2026
https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/192228
>From e39476bdf63d3dd07074c587fd0248042e85f292 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Wed, 15 Apr 2026 18:21:52 +0800
Subject: [PATCH 1/2] [clang-tidy][NFC] Fix list.rst and add_new_check.py
---
clang-tools-extra/clang-tidy/add_new_check.py | 11 ++++++++---
.../docs/clang-tidy/checks/cert/err33-c.rst | 2 ++
clang-tools-extra/docs/clang-tidy/checks/list.rst | 4 ++--
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/add_new_check.py b/clang-tools-extra/clang-tidy/add_new_check.py
index 53a9e49f8e4f2..d5962a56b7ac8 100755
--- a/clang-tools-extra/clang-tidy/add_new_check.py
+++ b/clang-tools-extra/clang-tidy/add_new_check.py
@@ -536,10 +536,15 @@ def format_link_alias(doc_file: Tuple[str, str]) -> str:
ref_begin = ""
ref_end = "_"
else:
- redirect_parts = re.search(r"^\.\./([^/]*)/([^/]*)$", match.group(1))
+ # Match either "../modernize/use-nullptr" or a same-directory
+ # redirect like "prefer-single-char-overloads".
+ redirect_parts = re.search(
+ r"^(?:\.\./([^/]+)/)?([^/]+)$", match.group(1)
+ )
assert redirect_parts
- title = redirect_parts[1] + "-" + redirect_parts[2]
- target = redirect_parts[1] + "/" + redirect_parts[2]
+ redirect_module = redirect_parts[1] or module
+ title = redirect_module + "-" + redirect_parts[2]
+ target = redirect_module + "/" + redirect_parts[2]
autofix = has_auto_fix(title)
ref_begin = ":doc:"
ref_end = ""
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
index 75da669c0a2b3..f04a47321efad 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
@@ -1,4 +1,6 @@
.. title:: clang-tidy - cert-err33-c
+.. meta::
+ :http-equiv=refresh: 5;URL=../bugprone/unused-return-value.html
cert-err33-c
============
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index df869d422f1a1..c7c37e08d67ea 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -242,7 +242,6 @@ Clang-Tidy Checks
:doc:`google-runtime-int <google/runtime-int>`,
:doc:`google-runtime-operator <google/runtime-operator>`,
:doc:`google-upgrade-googletest-case <google/upgrade-googletest-case>`, "Yes"
- :doc:`hicpp-exception-baseclass <hicpp/exception-baseclass>`,
:doc:`hicpp-multiway-paths-covered <hicpp/multiway-paths-covered>`,
:doc:`linuxkernel-must-check-errs <linuxkernel/must-check-errs>`,
:doc:`llvm-header-guard <llvm/header-guard>`,
@@ -352,7 +351,6 @@ Clang-Tidy Checks
:doc:`openmp-use-default-none <openmp/use-default-none>`,
:doc:`performance-avoid-endl <performance/avoid-endl>`, "Yes"
:doc:`performance-enum-size <performance/enum-size>`,
- :doc:`performance-faster-string-find <performance/faster-string-find>`, "Yes"
:doc:`performance-for-range-copy <performance/for-range-copy>`, "Yes"
:doc:`performance-implicit-conversion-in-loop <performance/implicit-conversion-in-loop>`,
:doc:`performance-inefficient-algorithm <performance/inefficient-algorithm>`, "Yes"
@@ -466,6 +464,7 @@ Check aliases
:doc:`cert-err60-cpp <cert/err60-cpp>`, :doc:`bugprone-exception-copy-constructor-throws <bugprone/exception-copy-constructor-throws>`,
:doc:`cert-err61-cpp <cert/err61-cpp>`, :doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`,
:doc:`cert-exp42-c <cert/exp42-c>`, :doc:`bugprone-suspicious-memory-comparison <bugprone/suspicious-memory-comparison>`,
+ :doc:`cert-exp45-c <cert/exp45-c>`, :doc:`bugprone-assignment-in-selection-statement <bugprone/assignment-in-selection-statement>`,
:doc:`cert-fio38-c <cert/fio38-c>`, :doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
:doc:`cert-flp30-c <cert/flp30-c>`, :doc:`bugprone-float-loop-counter <bugprone/float-loop-counter>`,
:doc:`cert-flp37-c <cert/flp37-c>`, :doc:`bugprone-suspicious-memory-comparison <bugprone/suspicious-memory-comparison>`,
@@ -633,3 +632,4 @@ Check aliases
:doc:`hicpp-vararg <hicpp/vararg>`, :doc:`cppcoreguidelines-pro-type-vararg <cppcoreguidelines/pro-type-vararg>`,
:doc:`llvm-else-after-return <llvm/else-after-return>`, :doc:`readability-else-after-return <readability/else-after-return>`, "Yes"
:doc:`llvm-qualified-auto <llvm/qualified-auto>`, :doc:`readability-qualified-auto <readability/qualified-auto>`, "Yes"
+ :doc:`performance-faster-string-find <performance/faster-string-find>`, :doc:`performance-prefer-single-char-overloads <performance/prefer-single-char-overloads>`, "Yes"
>From 036e5fdda0bc25a13d813057a5bd37a8d007ec9f Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Wed, 15 Apr 2026 21:15:33 +0800
Subject: [PATCH 2/2] better?
---
clang-tools-extra/clang-tidy/add_new_check.py | 154 +++++++++++-------
.../docs/clang-tidy/checks/cert/err33-c.rst | 3 -
2 files changed, 95 insertions(+), 62 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/add_new_check.py b/clang-tools-extra/clang-tidy/add_new_check.py
index d5962a56b7ac8..e40f673e753e7 100755
--- a/clang-tools-extra/clang-tidy/add_new_check.py
+++ b/clang-tools-extra/clang-tidy/add_new_check.py
@@ -494,7 +494,40 @@ def has_auto_fix(check_name: str) -> str:
return ""
- def process_doc(doc_file: Tuple[str, str]) -> Tuple[str, Optional[Match[str]]]:
+ def detect_alias_target(check_name: str, content: str) -> Optional[str]:
+ """Return the :doc: target for non-redirect alias pages.
+
+ This recognizes pages that keep their own documentation content, but
+ whose paragraph explicitly states that the current check is an
+ alias of another check.
+ """
+ paragraphs = [
+ re.sub(r"\s+", " ", paragraph.strip())
+ for paragraph in re.split(r"\n\s*\n", content)
+ if paragraph.strip()
+ ]
+
+ self_alias = re.compile(
+ r"^This check is an alias(?: of check| for)\b",
+ re.IGNORECASE,
+ )
+ named_alias = re.compile(
+ rf"^The\s+`?{re.escape(check_name)}(?:\s+check)?`?"
+ rf"(?:\s+check)?\s+is\s+an\s+alias,?\s+please\s+see\b",
+ re.IGNORECASE,
+ )
+
+ for paragraph in paragraphs:
+ if self_alias.search(paragraph) or named_alias.search(paragraph):
+ if match := re.search(r":doc:`[^`<]+?\s*<([^>]+)>`", paragraph):
+ return match.group(1)
+ if match := re.search(
+ r"`[^`<]+?\s*<(.+?)\.html(?:#[^>]+)?>`_", paragraph
+ ):
+ return match.group(1)
+ return None
+
+ def process_doc(doc_file: Tuple[str, str]) -> Tuple[str, Optional[str]]:
check_name = doc_file[0] + "-" + doc_file[1].replace(".rst", "")
with open(os.path.join(docs_dir, *doc_file), "r", encoding="utf8") as doc:
@@ -504,9 +537,7 @@ def process_doc(doc_file: Tuple[str, str]) -> Tuple[str, Optional[Match[str]]]:
# Orphan page, don't list it.
return "", None
- match = re.search(r".*:http-equiv=refresh: \d+;URL=(.*).html(.*)", content)
- # Is it a redirect?
- return check_name, match
+ return check_name, detect_alias_target(check_name, content)
def format_link(doc_file: Tuple[str, str]) -> str:
check_name, match = process_doc(doc_file)
@@ -522,61 +553,66 @@ def format_link(doc_file: Tuple[str, str]) -> str:
def format_link_alias(doc_file: Tuple[str, str]) -> str:
check_name, match = process_doc(doc_file)
- if (match or (check_name.startswith("clang-analyzer-"))) and check_name:
- module = doc_file[0]
- check_file = doc_file[1].replace(".rst", "")
- if (
- not match
- or match.group(1) == "https://clang.llvm.org/docs/analyzer/checkers"
- ):
- title = "Clang Static Analyzer " + check_file
- # Preserve the anchor in checkers.html from group 2.
- target = "" if not match else match.group(1) + ".html" + match.group(2)
- autofix = ""
- ref_begin = ""
- ref_end = "_"
- else:
- # Match either "../modernize/use-nullptr" or a same-directory
- # redirect like "prefer-single-char-overloads".
- redirect_parts = re.search(
- r"^(?:\.\./([^/]+)/)?([^/]+)$", match.group(1)
- )
- assert redirect_parts
- redirect_module = redirect_parts[1] or module
- title = redirect_module + "-" + redirect_parts[2]
- target = redirect_module + "/" + redirect_parts[2]
- autofix = has_auto_fix(title)
- ref_begin = ":doc:"
- ref_end = ""
-
- if target:
- # The checker is just a redirect.
- return (
- " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n"
- % {
- "check_name": check_name,
- "module": module,
- "check_file": check_file,
- "target": target,
- "title": title,
- "autofix": autofix,
- "ref_begin": ref_begin,
- "ref_end": ref_end,
- }
- )
- else:
- # The checker is just a alias without redirect.
- return (
- " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n"
- % {
- "check_name": check_name,
- "module": module,
- "check_file": check_file,
- "title": title,
- "autofix": autofix,
- }
- )
- return ""
+ is_clang_analyzer = check_name.startswith("clang-analyzer-")
+ if not check_name or (not match and not is_clang_analyzer):
+ return ""
+
+ module = doc_file[0]
+ check_file = doc_file[1].replace(".rst", "")
+ if is_clang_analyzer:
+ title = "Clang Static Analyzer " + check_file
+ # Clang Static Analyzer aliases still need the external redirect
+ # target so list.rst can link to the upstream analyzer docs.
+ with open(os.path.join(docs_dir, *doc_file), "r", encoding="utf8") as doc:
+ content = doc.read()
+ redirect = re.search(
+ r".*:http-equiv=refresh: \d+;URL=(.*).html(.*)", content
+ )
+ # Preserve the anchor in checkers.html from group 2.
+ target = (
+ "" if not redirect else redirect.group(1) + ".html" + redirect.group(2)
+ )
+ autofix = ""
+ ref_begin = ""
+ ref_end = "_"
+ else:
+ # Match neighbour or current-directory doc targets.
+ redirect_parts = re.search(r"^(?:\.\./([^/]+)/)?([^/]+)$", match)
+ assert redirect_parts
+ redirect_module = redirect_parts[1] or module
+ title = redirect_module + "-" + redirect_parts[2]
+ target = redirect_module + "/" + redirect_parts[2]
+ autofix = has_auto_fix(title)
+ ref_begin = ":doc:"
+ ref_end = ""
+
+ if target:
+ # The checker is just a redirect.
+ return (
+ " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n"
+ % {
+ "check_name": check_name,
+ "module": module,
+ "check_file": check_file,
+ "target": target,
+ "title": title,
+ "autofix": autofix,
+ "ref_begin": ref_begin,
+ "ref_end": ref_end,
+ }
+ )
+
+ # The checker is just a alias without redirect.
+ return (
+ " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n"
+ % {
+ "check_name": check_name,
+ "module": module,
+ "check_file": check_file,
+ "title": title,
+ "autofix": autofix,
+ }
+ )
print("Updating %s..." % filename)
with open(filename, "w", encoding="utf8", newline="\n") as f:
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
index f04a47321efad..adfddc5c5838d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/err33-c.rst
@@ -1,7 +1,4 @@
.. title:: clang-tidy - cert-err33-c
-.. meta::
- :http-equiv=refresh: 5;URL=../bugprone/unused-return-value.html
-
cert-err33-c
============
More information about the cfe-commits
mailing list