https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/175735
>From 85c183395f01fd25bb299163735e4518d396dbf1 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Thu, 25 Dec 2025 14:31:27 +0800 Subject: [PATCH 1/5] [clang-tidy] Header check support for check_clang_tidy.py --- .../test/clang-tidy/check_clang_tidy.py | 189 +++++++++++++++--- .../modernize-concat-nested-namespaces.h | 7 +- .../Inputs/pass-by-value/header-with-fix.h | 1 + .../modernize/Inputs/pass-by-value/header.h | 3 + .../modernize/concat-nested-namespaces.cpp | 11 +- .../modernize/pass-by-value-header.cpp | 14 +- .../modernize/pass-by-value-multi-fixes.cpp | 15 +- .../Inputs/unnecessary-value-param/header.h | 2 + .../unnecessary-value-param-header.cpp | 9 +- .../readability/duplicate-include.cpp | 5 +- 10 files changed, 189 insertions(+), 67 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index b173ecf4fbdca..76899861c3a7c 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -49,7 +49,7 @@ import re import subprocess import sys -from typing import List, Tuple +from typing import Dict, List, Sequence, Tuple def write_file(file_name: str, text: str) -> None: @@ -93,6 +93,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.input_file_name = args.input_file_name self.check_name = args.check_name self.temp_file_name = args.temp_file_name + self.check_headers = args.check_headers self.original_file_name = self.temp_file_name + ".orig" self.expect_clang_tidy_error = args.expect_clang_tidy_error self.std = args.std @@ -121,6 +122,18 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.clang_extra_args = self.clang_tidy_extra_args[i + 1 :] self.clang_tidy_extra_args = self.clang_tidy_extra_args[:i] + self.check_header_map: Dict[str, str] = {} + self.header_dir: str = self.temp_file_name + ".headers" + if self.check_headers: + self.check_header_map = { + os.path.abspath( + os.path.join(self.header_dir, os.path.basename(h)) + ): os.path.abspath(h) + for h in self.check_headers + } + + self.clang_extra_args.insert(0, "-I" + self.header_dir) + # If the test does not specify a config style, force an empty one; otherwise # auto-detection logic can discover a ".clang-tidy" file that is not related to # the test. @@ -196,15 +209,36 @@ def get_prefixes(self) -> None: ) assert expect_diagnosis or self.expect_no_diagnosis + def _sanitize_content(self, text: str) -> str: + return re.sub("// *CHECK-[A-Z0-9\\-]*:[^\r\n]*", "//", text) + + def _filter_prefixes(self, prefixes: Sequence[str], check_file: str) -> List[str]: + if check_file == self.input_file_name: + content = self.input_text + else: + with open(check_file, "r", encoding="utf-8") as f: + content = f.read() + return [p for p in prefixes if p in content] + def prepare_test_inputs(self) -> None: # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while # avoiding empty lines which could potentially trigger formatting-related # checks. - cleaned_test = re.sub("// *CHECK-[A-Z0-9\\-]*:[^\r\n]*", "//", self.input_text) + cleaned_test = self._sanitize_content(self.input_text) write_file(self.temp_file_name, cleaned_test) write_file(self.original_file_name, cleaned_test) + if self.check_headers: + os.makedirs(self.header_dir, exist_ok=True) + + for temp_header_path, header in self.check_header_map.items(): + with open(header, "r", encoding="utf-8") as f: + cleaned_header = self._sanitize_content(f.read()) + + write_file(temp_header_path, cleaned_header) + write_file(temp_header_path + ".orig", cleaned_header) + def run_clang_tidy(self) -> str: args = ( [ @@ -241,6 +275,18 @@ def run_clang_tidy(self) -> str: diff_output = try_run( ["diff", "-u", self.original_file_name, self.temp_file_name], False ) + if self.check_headers: + for temp_header_path in self.check_header_map: + diff_output += try_run( + [ + "diff", + "-u", + temp_header_path + ".orig", + temp_header_path, + ], + False, + ) + print("------------------------------ Fixes -----------------------------") print(diff_output) print("------------------------------------------------------------------") @@ -250,35 +296,57 @@ def check_no_diagnosis(self, clang_tidy_output: str) -> None: if clang_tidy_output != "": sys.exit("No diagnostics were expected, but found the ones above") - def check_fixes(self) -> None: - if self.has_check_fixes: - try_run( - [ - "FileCheck", - "--input-file=" + self.temp_file_name, - self.input_file_name, - "--check-prefixes=" + ",".join(self.fixes.prefixes), - ( - "--match-full-lines" - if not self.match_partial_fixes - else "--strict-whitespace" # Keeping past behavior. - ), - ] - ) + def check_fixes(self, input_file: str = "", check_file: str = "") -> None: + if not check_file and not self.has_check_fixes: + return - def check_messages(self, clang_tidy_output: str) -> None: - if self.has_check_messages: - messages_file = self.temp_file_name + ".msg" - write_file(messages_file, clang_tidy_output) - try_run( - [ - "FileCheck", - "-input-file=" + messages_file, - self.input_file_name, - "-check-prefixes=" + ",".join(self.messages.prefixes), - "-implicit-check-not={{warning|error}}:", - ] - ) + input_file = input_file or self.temp_file_name + check_file = check_file or self.input_file_name + active_prefixes = self._filter_prefixes(self.fixes.prefixes, check_file) + + if not active_prefixes: + return + + try_run( + [ + "FileCheck", + "--input-file=" + input_file, + check_file, + "--check-prefixes=" + ",".join(active_prefixes), + ( + "--match-full-lines" + if not self.match_partial_fixes + else "--strict-whitespace" # Keeping past behavior. + ), + ] + ) + + def check_messages( + self, + clang_tidy_output: str, + messages_file: str = "", + check_file: str = "", + ) -> None: + if not check_file and not self.has_check_messages: + return + + messages_file = messages_file or (self.temp_file_name + ".msg") + check_file = check_file or self.input_file_name + + active_prefixes = self._filter_prefixes(self.messages.prefixes, check_file) + if not active_prefixes: + return + + write_file(messages_file, clang_tidy_output) + try_run( + [ + "FileCheck", + "-input-file=" + messages_file, + check_file, + "-check-prefixes=" + ",".join(active_prefixes), + "-implicit-check-not={{warning|error}}:", + ] + ) def check_notes(self, clang_tidy_output: str) -> None: if self.has_check_notes: @@ -299,12 +367,60 @@ def check_notes(self, clang_tidy_output: str) -> None: ] ) + def check_header_messages(self, clang_tidy_output: str) -> str: + """ + Filters and verifies clang-tidy diagnostics for headers. + + - Input: The raw diagnostic output from clang-tidy. + - Output: The diagnostic output intended for the main file + verification. + + This function separates messages belonging to headers specified by + `-check-header', verifies them using FileCheck against the header's + own rules, and returns the rest for further processing. + """ + if not self.check_headers: + return clang_tidy_output + + header_messages: Dict[str, List[str]] = { + t: [] for t in self.check_header_map.keys() + } + remaining_lines: List[str] = [] + current_file: str = "" + + for line in clang_tidy_output.splitlines(keepends=True): + if re.match(r"^\d+ warnings? generated\.", line): + continue + # Matches the beginning of a clang-tidy diagnostic line, + # which starts with "file_path:line:col: ". + match = re.match(r"^([^:]+):\d+:\d+: ", line) + if match: + abs_path = os.path.abspath(match.group(1)) + current_file = abs_path if abs_path in header_messages else "" + + header_messages.get(current_file, remaining_lines).append(line) + + for temp_header, messages_list in header_messages.items(): + original_header = self.check_header_map[temp_header] + messages = "".join(messages_list) + if not messages: + continue + + self.check_messages( + messages, + messages_file=temp_header + ".msg", + check_file=original_header, + ) + + return "".join(remaining_lines) + def run(self) -> None: self.read_input() if self.export_fixes is None: self.get_prefixes() self.prepare_test_inputs() clang_tidy_output = self.run_clang_tidy() + clang_tidy_output = self.check_header_messages(clang_tidy_output) if self.expect_no_diagnosis: self.check_no_diagnosis(clang_tidy_output) elif self.export_fixes is None: @@ -312,6 +428,12 @@ def run(self) -> None: self.check_messages(clang_tidy_output) self.check_notes(clang_tidy_output) + for temp_header, original_header in self.check_header_map.items(): + self.check_fixes( + input_file=temp_header, + check_file=original_header, + ) + CPP_STANDARDS = [ "c++98", @@ -370,6 +492,13 @@ def parse_arguments() -> Tuple[argparse.Namespace, List[str]]: type=csv, help="comma-separated list of FileCheck suffixes", ) + parser.add_argument( + "-check-header", + action="append", + dest="check_headers", + default=[], + help="Header files to check", + ) parser.add_argument( "-export-fixes", default=None, diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h index 703d7e05ca2e9..0d748a81c0067 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h @@ -1,8 +1,9 @@ +// CHECK-MESSAGES-NORMAL: :[[@LINE+1]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] namespace nn1 { namespace nn2 { -// CHECK-FIXES: namespace nn1::nn2 +// CHECK-FIXES-NORMAL: namespace nn1::nn2 { void t(); } // namespace nn2 } // namespace nn1 -// CHECK-FIXES: void t(); -// CHECK-FIXES-NEXT: } // namespace nn1::nn2 +// CHECK-FIXES-NORMAL: void t(); +// CHECK-FIXES-NORMAL-NEXT: } // namespace nn1::nn2 diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h index 62609e25f682a..98512f411fe61 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header-with-fix.h @@ -4,5 +4,6 @@ struct S { }; struct Foo { Foo(const S &s); + // CHECK-FIXES: Foo(S s); S s; }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h index c2103cb3fc7a1..e27104e70ac0b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/pass-by-value/header.h @@ -6,5 +6,8 @@ class ThreadId { struct A { A(const ThreadId &tid) : threadid(tid) {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value] + // CHECK-FIXES: #include <utility> + // CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {} ThreadId threadid; }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp index 35cb5503ba245..58ae66d0b389f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/concat-nested-namespaces.cpp @@ -1,14 +1,7 @@ -// RUN: mkdir -p %t.dir -// RUN: cp %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %t.dir/modernize-concat-nested-namespaces.h -// RUN: %check_clang_tidy -std=c++17 -check-suffix=NORMAL %s modernize-concat-nested-namespaces %t.dir/code -- -header-filter=".*" -- -I %t.dir -// RUN: FileCheck -input-file=%t.dir/modernize-concat-nested-namespaces.h %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h -check-prefix=CHECK-FIXES -// Restore header file and re-run with c++20: -// RUN: cp %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %t.dir/modernize-concat-nested-namespaces.h -// RUN: %check_clang_tidy -std=c++20 -check-suffixes=NORMAL,CPP20 %s modernize-concat-nested-namespaces %t.dir/code -- -header-filter=".*" -- -I %t.dir -// RUN: FileCheck -input-file=%t.dir/modernize-concat-nested-namespaces.h %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h -check-prefix=CHECK-FIXES +// RUN: %check_clang_tidy -std=c++17 -check-suffix=NORMAL -check-header %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %s modernize-concat-nested-namespaces %t -- -header-filter=".*" -- -I %S/Inputs/concat-nested-namespaces +// RUN: %check_clang_tidy -std=c++20 -check-suffixes=NORMAL,CPP20 -check-header %S/Inputs/concat-nested-namespaces/modernize-concat-nested-namespaces.h %s modernize-concat-nested-namespaces %t -- -header-filter=".*" -- -I %S/Inputs/concat-nested-namespaces #include "modernize-concat-nested-namespaces.h" -// CHECK-MESSAGES-NORMAL-DAG: modernize-concat-nested-namespaces.h:1:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] namespace n1 {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp index 461a6378d99c0..79165e51f9e14 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-header.cpp @@ -1,10 +1,6 @@ -// RUN: mkdir -p %t.dir -// RUN: cp %S/Inputs/pass-by-value/header.h %t.dir/pass-by-value-header.h -// RUN: clang-tidy %s -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %t.dir | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:" -// RUN: FileCheck -input-file=%t.dir/pass-by-value-header.h %s -check-prefix=CHECK-FIXES -// FIXME: Make the test work in all language modes. +// RUN: %check_clang_tidy -check-header %S/Inputs/pass-by-value/header.h \ +// RUN: %s modernize-pass-by-value %t -- -header-filter='.*' \ +// RUN: -- -I %S/Inputs/pass-by-value -std=c++11 -#include "pass-by-value-header.h" -// CHECK-MESSAGES: :8:5: warning: pass by value and use std::move [modernize-pass-by-value] -// CHECK-FIXES: #include <utility> -// CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {} +#include "header.h" +// FIXME: Make the test work in all language modes. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp index b77c74be02f5f..c5e6e0be30b8f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value-multi-fixes.cpp @@ -1,13 +1,10 @@ -// RUN: mkdir -p %t.dir -// RUN: cat %S/Inputs/pass-by-value/header-with-fix.h > %t.dir/pass-by-value-header-with-fix.h -// RUN: sed -e 's#//.*$##' %s > %t.dir/code.cpp -// RUN: clang-tidy %t.dir/code.cpp -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %t.dir | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:" -// RUN: FileCheck -input-file=%t.dir/code.cpp %s -check-prefix=CHECK-FIXES -// RUN: FileCheck -input-file=%t.dir/pass-by-value-header-with-fix.h %s -check-prefix=CHECK-HEADER-FIXES +// RUN: %check_clang_tidy -check-header %S/Inputs/pass-by-value/header-with-fix.h \ +// RUN: %s modernize-pass-by-value %t -- -header-filter='.*' \ +// RUN: -- -I %S/Inputs/pass-by-value -std=c++11 -#include "pass-by-value-header-with-fix.h" -// CHECK-HEADER-FIXES: Foo(S s); +#include "header-with-fix.h" + +// CHECK-MESSAGES: :[[@LINE+1]]:10: warning: pass by value and use std::move [modernize-pass-by-value] Foo::Foo(const S &s) : s(s) {} -// CHECK-MESSAGES: :10:10: warning: pass by value and use std::move [modernize-pass-by-value] // CHECK-FIXES: #include <utility> // CHECK-FIXES: Foo::Foo(S s) : s(std::move(s)) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h b/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h index d6f6e65ace79d..32b3d3374bc59 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/Inputs/unnecessary-value-param/header.h @@ -7,9 +7,11 @@ struct ABC { int f1(int n, ABC v1, ABC v2); // line 9 +// CHECK-FIXES: int f1(int n, const ABC& v1, const ABC& v2); // line 9 int f1(int n, ABC v1); // line 11 void f2( int n, ABC v2); // line 15 +// CHECK-FIXES: void f2( int n, const ABC& v2); // line 15 diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp index a7fc1eace67c5..ded66392e4af5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-header.cpp @@ -1,8 +1,7 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: cp %S/Inputs/unnecessary-value-param/header.h %t/header.h -// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t/temp -- -- -I %t -// RUN: diff %t/header.h %S/Inputs/unnecessary-value-param/header-fixed.h +// RUN: %check_clang_tidy -check-header %S/Inputs/unnecessary-value-param/header.h \ +// RUN: %s performance-unnecessary-value-param %t -- \ +// RUN: -header-filter='.*' \ +// RUN: -- -I %S/Inputs/unnecessary-value-param #include "header.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp index c452f69fad07d..8483e895bb635 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/duplicate-include.cpp @@ -1,5 +1,6 @@ -// RUN: %check_clang_tidy %s readability-duplicate-include %t -- \ -// RUN: -header-filter='' \ +// RUN: %check_clang_tidy -check-header %S/Inputs/duplicate-include/duplicate-include.h \ +// RUN: %s readability-duplicate-include %t -- \ +// RUN: -header-filter='.*' \ // RUN: -- -isystem %S/Inputs/duplicate-include/system -I %S/Inputs/duplicate-include int a; >From 5d93fbb08b5768841fe1f3924c06be0e0419043b Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Tue, 13 Jan 2026 18:51:18 +0800 Subject: [PATCH 2/5] Try to make Windows FS happy, not sure whether it will work :( --- clang-tools-extra/test/clang-tidy/check_clang_tidy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 76899861c3a7c..1dcb47ec38603 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -126,8 +126,8 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.header_dir: str = self.temp_file_name + ".headers" if self.check_headers: self.check_header_map = { - os.path.abspath( - os.path.join(self.header_dir, os.path.basename(h)) + os.path.normcase( + os.path.abspath(os.path.join(self.header_dir, os.path.basename(h))) ): os.path.abspath(h) for h in self.check_headers } @@ -395,7 +395,7 @@ def check_header_messages(self, clang_tidy_output: str) -> str: # which starts with "file_path:line:col: ". match = re.match(r"^([^:]+):\d+:\d+: ", line) if match: - abs_path = os.path.abspath(match.group(1)) + abs_path = os.path.normcase(os.path.abspath(match.group(1))) current_file = abs_path if abs_path in header_messages else "" header_messages.get(current_file, remaining_lines).append(line) >From 5b9cb8e54a0afcbb1d0f8f192fa6301853805be1 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Tue, 13 Jan 2026 19:08:04 +0800 Subject: [PATCH 3/5] Let's try backtracking.. --- clang-tools-extra/test/clang-tidy/check_clang_tidy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 1dcb47ec38603..2bbf04bbc143a 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -393,7 +393,7 @@ def check_header_messages(self, clang_tidy_output: str) -> str: continue # Matches the beginning of a clang-tidy diagnostic line, # which starts with "file_path:line:col: ". - match = re.match(r"^([^:]+):\d+:\d+: ", line) + match = re.match(r"^(.+):\d+:\d+: ", line) if match: abs_path = os.path.normcase(os.path.abspath(match.group(1))) current_file = abs_path if abs_path in header_messages else "" >From 298460d591b5d43d14a45e9496cfee492fbaf580 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Fri, 16 Jan 2026 19:09:06 +0800 Subject: [PATCH 4/5] ~ --- .../test/clang-tidy/check_clang_tidy.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 2bbf04bbc143a..b0797cce8da60 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -49,6 +49,7 @@ import re import subprocess import sys +from collections import defaultdict from typing import Dict, List, Sequence, Tuple @@ -123,7 +124,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.clang_tidy_extra_args = self.clang_tidy_extra_args[:i] self.check_header_map: Dict[str, str] = {} - self.header_dir: str = self.temp_file_name + ".headers" + self.header_dir = f"{self.temp_file_name}.headers" if self.check_headers: self.check_header_map = { os.path.normcase( @@ -314,9 +315,9 @@ def check_fixes(self, input_file: str = "", check_file: str = "") -> None: check_file, "--check-prefixes=" + ",".join(active_prefixes), ( - "--match-full-lines" - if not self.match_partial_fixes - else "--strict-whitespace" # Keeping past behavior. + "--strict-whitespace" # Keeping past behavior. + if self.match_partial_fixes + else "--match-full-lines" ), ] ) @@ -330,7 +331,7 @@ def check_messages( if not check_file and not self.has_check_messages: return - messages_file = messages_file or (self.temp_file_name + ".msg") + messages_file = messages_file or f"{self.temp_file_name}.msg" check_file = check_file or self.input_file_name active_prefixes = self._filter_prefixes(self.messages.prefixes, check_file) @@ -382,9 +383,7 @@ def check_header_messages(self, clang_tidy_output: str) -> str: if not self.check_headers: return clang_tidy_output - header_messages: Dict[str, List[str]] = { - t: [] for t in self.check_header_map.keys() - } + header_messages = defaultdict(list) remaining_lines: List[str] = [] current_file: str = "" @@ -396,13 +395,15 @@ def check_header_messages(self, clang_tidy_output: str) -> str: match = re.match(r"^(.+):\d+:\d+: ", line) if match: abs_path = os.path.normcase(os.path.abspath(match.group(1))) - current_file = abs_path if abs_path in header_messages else "" + current_file = abs_path if abs_path in self.check_header_map else "" - header_messages.get(current_file, remaining_lines).append(line) + dest_list = ( + header_messages[current_file] if current_file else remaining_lines + ) + dest_list.append(line) - for temp_header, messages_list in header_messages.items(): - original_header = self.check_header_map[temp_header] - messages = "".join(messages_list) + for temp_header, original_header in self.check_header_map.items(): + messages = "".join(header_messages[temp_header]) if not messages: continue >From 7930f0455c9f526cbfd4befd0b7dd7aace4a834a Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Tue, 20 Jan 2026 20:20:50 +0800 Subject: [PATCH 5/5] Address review feedback --- .../test/clang-tidy/check_clang_tidy.py | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index b0797cce8da60..4ffce5a5463e3 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -95,7 +95,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: self.check_name = args.check_name self.temp_file_name = args.temp_file_name self.check_headers = args.check_headers - self.original_file_name = self.temp_file_name + ".orig" + self.original_file_name = f"{self.temp_file_name}.orig" self.expect_clang_tidy_error = args.expect_clang_tidy_error self.std = args.std self.check_suffix = args.check_suffix @@ -133,7 +133,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: for h in self.check_headers } - self.clang_extra_args.insert(0, "-I" + self.header_dir) + self.clang_extra_args.insert(0, f"-I{self.header_dir}") # If the test does not specify a config style, force an empty one; otherwise # auto-detection logic can discover a ".clang-tidy" file that is not related to @@ -150,7 +150,7 @@ def __init__(self, args: argparse.Namespace, extra_args: List[str]) -> None: "-fblocks", ] + self.clang_extra_args - self.clang_extra_args.append("-std=" + self.std) + self.clang_extra_args.append(f"-std={self.std}") # Tests should not rely on STL being available, and instead provide mock # implementations of relevant APIs. @@ -172,8 +172,8 @@ def get_prefixes(self) -> None: for suffix in self.check_suffix: if suffix and not re.match("^[A-Z0-9\\-]+$", suffix): sys.exit( - 'Only A..Z, 0..9 and "-" are allowed in check suffixes list,' - + ' but "%s" was given' % suffix + 'Only A..Z, 0..9 and "-" are allowed in check suffixes list, ' + f'but "{suffix}" was given' ) file_check_suffix = ("-" + suffix) if suffix else "" @@ -238,7 +238,7 @@ def prepare_test_inputs(self) -> None: cleaned_header = self._sanitize_content(f.read()) write_file(temp_header_path, cleaned_header) - write_file(temp_header_path + ".orig", cleaned_header) + write_file(f"{temp_header_path}.orig", cleaned_header) def run_clang_tidy(self) -> str: args = ( @@ -251,11 +251,11 @@ def run_clang_tidy(self) -> str: ( "-fix" if self.export_fixes is None - else "--export-fixes=" + self.export_fixes + else f"--export-fixes={self.export_fixes}" ) ] + [ - "--checks=-*," + self.check_name, + f"--checks=-*,{self.check_name}", ] + self.clang_tidy_extra_args + ["--"] @@ -263,7 +263,7 @@ def run_clang_tidy(self) -> str: ) if self.expect_clang_tidy_error: args.insert(0, "not") - print("Running " + repr(args) + "...") + print(f"Running {repr(args)}...") clang_tidy_output = try_run(args) print("------------------------ clang-tidy output -----------------------") print( @@ -279,13 +279,7 @@ def run_clang_tidy(self) -> str: if self.check_headers: for temp_header_path in self.check_header_map: diff_output += try_run( - [ - "diff", - "-u", - temp_header_path + ".orig", - temp_header_path, - ], - False, + ["diff", "-u", f"{temp_header_path}.orig", temp_header_path], False ) print("------------------------------ Fixes -----------------------------") @@ -311,9 +305,9 @@ def check_fixes(self, input_file: str = "", check_file: str = "") -> None: try_run( [ "FileCheck", - "--input-file=" + input_file, + f"--input-file={input_file}", check_file, - "--check-prefixes=" + ",".join(active_prefixes), + f"--check-prefixes={','.join(active_prefixes)}", ( "--strict-whitespace" # Keeping past behavior. if self.match_partial_fixes @@ -342,16 +336,16 @@ def check_messages( try_run( [ "FileCheck", - "-input-file=" + messages_file, + f"-input-file={messages_file}", check_file, - "-check-prefixes=" + ",".join(active_prefixes), + f"-check-prefixes={','.join(active_prefixes)}", "-implicit-check-not={{warning|error}}:", ] ) def check_notes(self, clang_tidy_output: str) -> None: if self.has_check_notes: - notes_file = self.temp_file_name + ".notes" + notes_file = f"{self.temp_file_name}.notes" filtered_output = [ line for line in clang_tidy_output.splitlines() @@ -361,9 +355,9 @@ def check_notes(self, clang_tidy_output: str) -> None: try_run( [ "FileCheck", - "-input-file=" + notes_file, + f"-input-file={notes_file}", self.input_file_name, - "-check-prefixes=" + ",".join(self.notes.prefixes), + f"-check-prefixes={','.join(self.notes.prefixes)}", "-implicit-check-not={{note|warning|error}}:", ] ) @@ -408,9 +402,7 @@ def check_header_messages(self, clang_tidy_output: str) -> str: continue self.check_messages( - messages, - messages_file=temp_header + ".msg", - check_file=original_header, + messages, messages_file=f"{temp_header}.msg", check_file=original_header ) return "".join(remaining_lines) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
