[clang-tools-extra] [clang-tidy] Improved --verify-config when using literal style in config file (PR #85591)

FĂ©lix-Antoine Constantin via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 23 10:31:57 PDT 2024


https://github.com/felix642 updated https://github.com/llvm/llvm-project/pull/85591

>From f015496c511b4c2898b9f4d65ebfe39a34c5119c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix-Antoine=20Constantin?=
 <felix-antoine.constantin at polymtl.ca>
Date: Sun, 17 Mar 2024 20:50:17 -0400
Subject: [PATCH 1/3] [clang-tidy] Improved --verify-config when using literal
 style in config file

Specifying checks using the literal style (|) in the clang-tidy config file is currently
supported but was not implemented for the --verify-config options.
This means that clang-tidy would work properly but, using the --verify-config option
would raise an error due to some checks not being parsed properly.

Fixes #53737
---
 clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp  |  9 ++++++---
 clang-tools-extra/docs/ReleaseNotes.rst              |  3 +++
 .../test/clang-tidy/infrastructure/verify-config.cpp | 12 ++++++++++++
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 9f3d6b6db6cbca..b68a6215b383a6 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -454,11 +454,14 @@ static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";
 
 static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,
                          StringRef Source) {
-  llvm::StringRef Cur, Rest;
+  llvm::StringRef Cur = CheckGlob;
+  llvm::StringRef Rest = CheckGlob;
   bool AnyInvalid = false;
-  for (std::tie(Cur, Rest) = CheckGlob.split(',');
-       !(Cur.empty() && Rest.empty()); std::tie(Cur, Rest) = Rest.split(',')) {
+  while (!Cur.empty() || !Rest.empty()) {
+    Cur = Rest.substr(0, Rest.find_first_of(",\n"));
+    Rest = Rest.substr(Cur.size() + 1);
     Cur = Cur.trim();
+
     if (Cur.empty())
       continue;
     Cur.consume_front("-");
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index a604e9276668ae..3887384e713896 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -262,6 +262,9 @@ Miscellaneous
   option is specified. Now ``clang-apply-replacements`` applies formatting only with
   the option.
 
+- Fixed ``--verify-check`` option not properly parsing checks when using the 
+  literal operator in the ``.clang-tidy`` config
+
 Improvements to include-fixer
 -----------------------------
 
diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp
index 421f8641281acb..3659285986482a 100644
--- a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp
+++ b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp
@@ -18,3 +18,15 @@
 // CHECK-VERIFY: command-line option '-checks': warning: check glob 'bad*glob' doesn't match any known check [-verify-config]
 // CHECK-VERIFY: command-line option '-checks': warning: unknown check 'llvm-includeorder'; did you mean 'llvm-include-order' [-verify-config]
 // CHECK-VERIFY: command-line option '-checks': warning: unknown check 'my-made-up-check' [-verify-config]
+
+// RUN: echo -e 'Checks: |\n bugprone-argument-comment\n bugprone-assert-side-effect,\n bugprone-bool-pointer-implicit-conversion\n readability-use-anyof*' > %T/MyClangTidyConfig
+// RUN: clang-tidy -verify-config \
+// RUN: --config-file=%T/MyClangTidyConfig | FileCheck %s -check-prefix=CHECK-VERIFY-BLOCK-OK
+// CHECK-VERIFY-BLOCK-OK: No config errors detected.
+
+// RUN: echo -e 'Checks: |\n bugprone-arguments-*\n bugprone-assert-side-effects\n bugprone-bool-pointer-implicit-conversion' > %T/MyClangTidyConfigBad
+// RUN: not clang-tidy -verify-config \
+// RUN: --config-file=%T/MyClangTidyConfigBad 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-BLOCK-BAD
+// CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: check glob 'bugprone-arguments-*' doesn't match any known check [-verify-config]
+// CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: unknown check 'bugprone-assert-side-effects'; did you mean 'bugprone-assert-side-effect' [-verify-config]
+

>From 0e2cb54934829f42069b06f599fa9e724bdf40b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix-Antoine=20Constantin?=
 <felix-antoine.constantin at polymtl.ca>
Date: Sat, 23 Mar 2024 13:30:28 -0400
Subject: [PATCH 2/3] fixup! [clang-tidy] Improved --verify-config when using
 literal style in config file

Partially rewrote verifyChecks() to realign the checks parsing logic.

Instead of manually parsing the string of globs, verifyCHecks() now uses a GlobList which is responsible
to parse the string and return a list of regexes. This ensures that any changes made to the checks parsing logic
has to be made at only one place rather than two.
---
 clang-tools-extra/clang-tidy/GlobList.cpp     | 14 ++--
 clang-tools-extra/clang-tidy/GlobList.h       |  4 ++
 .../clang-tidy/tool/ClangTidyMain.cpp         | 64 ++++++-------------
 3 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/GlobList.cpp b/clang-tools-extra/clang-tidy/GlobList.cpp
index dfe3f7c505b174..7b8abcdafd2938 100644
--- a/clang-tools-extra/clang-tidy/GlobList.cpp
+++ b/clang-tools-extra/clang-tidy/GlobList.cpp
@@ -19,12 +19,17 @@ static bool consumeNegativeIndicator(StringRef &GlobList) {
   return GlobList.consume_front("-");
 }
 
-// Converts first glob from the comma-separated list of globs to Regex and
-// removes it and the trailing comma from the GlobList.
-static llvm::Regex consumeGlob(StringRef &GlobList) {
+// Extract the first glob from the comma-separated list of globs
+// removes it and the trailing comma from the GlobList and
+// returns the extracted glob.
+static llvm::StringRef extractNextGlob(StringRef &GlobList) {
   StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find_first_of(",\n"));
   StringRef Glob = UntrimmedGlob.trim();
   GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
+  return Glob;
+}
+
+static llvm::Regex createRegexFromGlob(StringRef &Glob) {
   SmallString<128> RegexText("^");
   StringRef MetaChars("()^$|*+?.[]\\{}");
   for (char C : Glob) {
@@ -43,7 +48,8 @@ GlobList::GlobList(StringRef Globs, bool KeepNegativeGlobs /* =true */) {
   do {
     GlobListItem Item;
     Item.IsPositive = !consumeNegativeIndicator(Globs);
-    Item.Regex = consumeGlob(Globs);
+    Item.Text = extractNextGlob(Globs);
+    Item.Regex = createRegexFromGlob(Item.Text);
     if (Item.IsPositive || KeepNegativeGlobs)
       Items.push_back(std::move(Item));
   } while (!Globs.empty());
diff --git a/clang-tools-extra/clang-tidy/GlobList.h b/clang-tools-extra/clang-tidy/GlobList.h
index 44af182e43b002..4317928270adff 100644
--- a/clang-tools-extra/clang-tidy/GlobList.h
+++ b/clang-tools-extra/clang-tidy/GlobList.h
@@ -44,8 +44,12 @@ class GlobList {
   struct GlobListItem {
     bool IsPositive;
     llvm::Regex Regex;
+    llvm::StringRef Text;
   };
   SmallVector<GlobListItem, 0> Items;
+
+public:
+  const SmallVectorImpl<GlobListItem> &getItems() const { return Items; };
 };
 
 /// A \p GlobList that caches search results, so that search is performed only
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index b68a6215b383a6..16f37c073682cf 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -454,55 +454,31 @@ static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";
 
 static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,
                          StringRef Source) {
-  llvm::StringRef Cur = CheckGlob;
-  llvm::StringRef Rest = CheckGlob;
+  GlobList Globs(CheckGlob);
   bool AnyInvalid = false;
-  while (!Cur.empty() || !Rest.empty()) {
-    Cur = Rest.substr(0, Rest.find_first_of(",\n"));
-    Rest = Rest.substr(Cur.size() + 1);
-    Cur = Cur.trim();
-
-    if (Cur.empty())
-      continue;
-    Cur.consume_front("-");
-    if (Cur.starts_with("clang-diagnostic"))
+  for (const auto &Item : Globs.getItems()) {
+    const llvm::Regex &Reg = Item.Regex;
+    const llvm::StringRef Text = Item.Text;
+    if (Text.starts_with("clang-diagnostic"))
       continue;
-    if (Cur.contains('*')) {
-      SmallString<128> RegexText("^");
-      StringRef MetaChars("()^$|*+?.[]\\{}");
-      for (char C : Cur) {
-        if (C == '*')
-          RegexText.push_back('.');
-        else if (MetaChars.contains(C))
-          RegexText.push_back('\\');
-        RegexText.push_back(C);
-      }
-      RegexText.push_back('$');
-      llvm::Regex Glob(RegexText);
-      std::string Error;
-      if (!Glob.isValid(Error)) {
-        AnyInvalid = true;
-        llvm::WithColor::error(llvm::errs(), Source)
-            << "building check glob '" << Cur << "' " << Error << "'\n";
-        continue;
-      }
-      if (llvm::none_of(AllChecks.keys(),
-                        [&Glob](StringRef S) { return Glob.match(S); })) {
-        AnyInvalid = true;
+    if (llvm::none_of(AllChecks.keys(), [&Reg](StringRef S) {
+          llvm::errs() << S << '\n';
+          return Reg.match(S);
+        })) {
+      AnyInvalid = true;
+      if (Item.Text.contains('*'))
         llvm::WithColor::warning(llvm::errs(), Source)
-            << "check glob '" << Cur << "' doesn't match any known check"
+            << "check glob '" << Text << "' doesn't match any known check"
             << VerifyConfigWarningEnd;
+      else {
+        llvm::raw_ostream &Output =
+            llvm::WithColor::warning(llvm::errs(), Source)
+            << "unknown check '" << Text << '\'';
+        llvm::StringRef Closest = closest(Text, AllChecks);
+        if (!Closest.empty())
+          Output << "; did you mean '" << Closest << '\'';
+        Output << VerifyConfigWarningEnd;
       }
-    } else {
-      if (AllChecks.contains(Cur))
-        continue;
-      AnyInvalid = true;
-      llvm::raw_ostream &Output = llvm::WithColor::warning(llvm::errs(), Source)
-                                  << "unknown check '" << Cur << '\'';
-      llvm::StringRef Closest = closest(Cur, AllChecks);
-      if (!Closest.empty())
-        Output << "; did you mean '" << Closest << '\'';
-      Output << VerifyConfigWarningEnd;
     }
   }
   return AnyInvalid;

>From 7fe3a903e705dc310948be30d16ce95be1519e6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix-Antoine=20Constantin?=
 <felix-antoine.constantin at polymtl.ca>
Date: Sat, 23 Mar 2024 13:31:25 -0400
Subject: [PATCH 3/3] fixup! [clang-tidy] Improved --verify-config when using
 literal style in config file

Fixed small type in release notes
---
 clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3887384e713896..6b2a2d0cdcf12a 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -263,7 +263,7 @@ Miscellaneous
   the option.
 
 - Fixed ``--verify-check`` option not properly parsing checks when using the 
-  literal operator in the ``.clang-tidy`` config
+  literal operator in the ``.clang-tidy`` config.
 
 Improvements to include-fixer
 -----------------------------



More information about the cfe-commits mailing list