[clang-tools-extra] [clang-tidy] support query based custom check (PR #131804)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 18 06:57:11 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
Author: Congcong Cai (HerrCai0907)
<details>
<summary>Changes</summary>
---
Patch is 31.84 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131804.diff
25 Files Affected:
- (modified) clang-tools-extra/clang-tidy/CMakeLists.txt (+2)
- (modified) clang-tools-extra/clang-tidy/ClangTidy.cpp (+8-1)
- (modified) clang-tools-extra/clang-tidy/ClangTidyForceLinker.h (+5)
- (modified) clang-tools-extra/clang-tidy/ClangTidyModule.h (+2)
- (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.cpp (+53-3)
- (modified) clang-tools-extra/clang-tidy/ClangTidyOptions.h (+15)
- (added) clang-tools-extra/clang-tidy/custom/CMakeLists.txt (+20)
- (added) clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp (+50)
- (added) clang-tools-extra/clang-tidy/custom/QueryCheck.cpp (+102)
- (added) clang-tools-extra/clang-tidy/custom/QueryCheck.h (+42)
- (modified) clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp (+2)
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+3)
- (added) clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst (+57)
- (modified) clang-tools-extra/docs/clang-tidy/index.rst (+3)
- (added) clang-tools-extra/test/clang-tidy/checkers/custom/Inputs/clang-tidy.yml (+22)
- (added) clang-tools-extra/test/clang-tidy/checkers/custom/Inputs/incorrect-clang-tidy.yml (+10)
- (added) clang-tools-extra/test/clang-tidy/checkers/custom/query-incorrect-query.cpp (+3)
- (added) clang-tools-extra/test/clang-tidy/checkers/custom/query-partially-active-check.cpp (+5)
- (added) clang-tools-extra/test/clang-tidy/checkers/custom/query.cpp (+7)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/Inputs/custom-query-check/append-clang-tidy.yml (+8)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/Inputs/custom-query-check/empty-clang-tidy.yml (+1)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/Inputs/custom-query-check/override-clang-tidy.yml (+11)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/Inputs/custom-query-check/root-clang-tidy.yml (+11)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/Inputs/custom-query-check/vfsoverlay.yaml (+44)
- (added) clang-tools-extra/test/clang-tidy/infrastructure/custom-query-check.cpp (+45)
``````````diff
diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt
index 93117cf1d6373..90efd2ef1f451 100644
--- a/clang-tools-extra/clang-tidy/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/CMakeLists.txt
@@ -58,6 +58,7 @@ add_subdirectory(bugprone)
add_subdirectory(cert)
add_subdirectory(concurrency)
add_subdirectory(cppcoreguidelines)
+add_subdirectory(custom)
add_subdirectory(darwin)
add_subdirectory(fuchsia)
add_subdirectory(google)
@@ -85,6 +86,7 @@ set(ALL_CLANG_TIDY_CHECKS
clangTidyCERTModule
clangTidyConcurrencyModule
clangTidyCppCoreGuidelinesModule
+ clangTidyCustomModule
clangTidyDarwinModule
clangTidyFuchsiaModule
clangTidyGoogleModule
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index d99847a82d168..f2a69e01a32c5 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -57,6 +57,11 @@ LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
namespace clang::tidy {
+namespace custom {
+extern void registerCustomChecks(ClangTidyOptions const &O,
+ ClangTidyCheckFactories &Factories);
+} // namespace custom
+
namespace {
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
@@ -346,6 +351,7 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
: Context(Context), OverlayFS(std::move(OverlayFS)),
CheckFactories(new ClangTidyCheckFactories) {
+ custom::registerCustomChecks(Context.getOptions(), *CheckFactories);
for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
std::unique_ptr<ClangTidyModule> Module = E.instantiate();
Module->addCheckFactories(*CheckFactories);
@@ -416,7 +422,7 @@ ClangTidyASTConsumerFactory::createASTConsumer(
.getCurrentWorkingDirectory();
if (WorkingDir)
Context.setCurrentBuildDirectory(WorkingDir.get());
-
+ custom::registerCustomChecks(Context.getOptions(), *CheckFactories);
std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
CheckFactories->createChecksForLanguage(&Context);
@@ -659,6 +665,7 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), Opts),
AllowEnablingAnalyzerAlphaCheckers);
ClangTidyCheckFactories Factories;
+ custom::registerCustomChecks(Context.getOptions(), Factories);
for (const ClangTidyModuleRegistry::entry &Module :
ClangTidyModuleRegistry::entries()) {
Module.instantiate()->addCheckFactories(Factories);
diff --git a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h
index adde9136ff1dd..50ac6e138df18 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h
@@ -54,6 +54,11 @@ extern volatile int CppCoreGuidelinesModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
CppCoreGuidelinesModuleAnchorSource;
+// This anchor is used to force the linker to link the CustomModule.
+extern volatile int CustomModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CustomModuleAnchorDestination =
+ CustomModuleAnchorSource;
+
// This anchor is used to force the linker to link the DarwinModule.
extern volatile int DarwinModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED DarwinModuleAnchorDestination =
diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h
index 28f54331755a7..6f0b2bf32a291 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyModule.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyModule.h
@@ -62,6 +62,8 @@ class ClangTidyCheckFactories {
});
}
+ void erase(llvm::StringRef CheckName) { Factories.erase(CheckName); }
+
/// Create instances of checks that are enabled.
std::vector<std::unique_ptr<ClangTidyCheck>>
createChecks(ClangTidyContext *Context) const;
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
index 8bac6f161fa05..acedbd8d41faa 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
@@ -8,12 +8,12 @@
#include "ClangTidyOptions.h"
#include "ClangTidyModuleRegistry.h"
+#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
-#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
@@ -72,7 +72,8 @@ struct NOptionMap {
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap) {
Options.reserve(OptionMap.size());
for (const auto &KeyValue : OptionMap)
- Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value);
+ Options.emplace_back(std::string(KeyValue.getKey()),
+ KeyValue.getValue().Value);
}
ClangTidyOptions::OptionMap denormalize(IO &) {
ClangTidyOptions::OptionMap Map;
@@ -126,6 +127,52 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool,
}
}
+namespace {
+struct MultiLineString {
+ std::string &S;
+};
+} // namespace
+
+template <> struct BlockScalarTraits<MultiLineString> {
+ static void output(const MultiLineString &S, void *Ctxt, raw_ostream &OS) {
+ OS << S.S;
+ }
+ static StringRef input(StringRef Str, void *Ctxt, MultiLineString &S) {
+ S.S = Str;
+ return "";
+ }
+};
+
+template <> struct ScalarEnumerationTraits<clang::DiagnosticIDs::Level> {
+ static void enumeration(IO &IO, clang::DiagnosticIDs::Level &Level) {
+ IO.enumCase(Level, "Error", clang::DiagnosticIDs::Level::Error);
+ IO.enumCase(Level, "Warning", clang::DiagnosticIDs::Level::Warning);
+ IO.enumCase(Level, "Note", clang::DiagnosticIDs::Level::Note);
+ }
+};
+template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckDiag> {
+ static const bool flow = false;
+};
+template <> struct MappingTraits<ClangTidyOptions::CustomCheckDiag> {
+ static void mapping(IO &IO, ClangTidyOptions::CustomCheckDiag &D) {
+ IO.mapRequired("BindName", D.BindName);
+ MultiLineString MLS{D.Message};
+ IO.mapRequired("Message", MLS);
+ IO.mapOptional("Level", D.Level);
+ }
+};
+template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckValue> {
+ static const bool flow = false;
+};
+template <> struct MappingTraits<ClangTidyOptions::CustomCheckValue> {
+ static void mapping(IO &IO, ClangTidyOptions::CustomCheckValue &V) {
+ IO.mapRequired("Name", V.Name);
+ MultiLineString MLS{V.Query};
+ IO.mapRequired("Query", MLS);
+ IO.mapRequired("Diagnostic", V.Diags);
+ }
+};
+
struct ChecksVariant {
std::optional<std::string> AsString;
std::optional<std::vector<std::string>> AsVector;
@@ -181,6 +228,7 @@ template <> struct MappingTraits<ClangTidyOptions> {
IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
IO.mapOptional("UseColor", Options.UseColor);
IO.mapOptional("SystemHeaders", Options.SystemHeaders);
+ IO.mapOptional("CustomChecks", Options.CustomChecks);
}
};
@@ -249,6 +297,8 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
ClangTidyValue(KeyValue.getValue().Value,
KeyValue.getValue().Priority + Order));
}
+ mergeVectors(CustomChecks, Other.CustomChecks);
+
return *this;
}
diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
index dd78c570d25d9..2a64ee8fdf7ea 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h
@@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
+#include "clang/Basic/DiagnosticIDs.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
@@ -17,6 +18,7 @@
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <functional>
+#include <map>
#include <optional>
#include <string>
#include <system_error>
@@ -129,6 +131,19 @@ struct ClangTidyOptions {
/// Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
+ struct CustomCheckDiag {
+ std::string BindName;
+ std::string Message;
+ std::optional<DiagnosticIDs::Level> Level;
+ };
+ struct CustomCheckValue {
+ std::string Name;
+ std::string Query;
+ llvm::SmallVector<CustomCheckDiag> Diags;
+ };
+ using CustomCheckValueList = llvm::SmallVector<CustomCheckValue>;
+ std::optional<CustomCheckValueList> CustomChecks;
+
using ArgList = std::vector<std::string>;
/// Add extra compilation arguments to the end of the list.
diff --git a/clang-tools-extra/clang-tidy/custom/CMakeLists.txt b/clang-tools-extra/clang-tidy/custom/CMakeLists.txt
new file mode 100644
index 0000000000000..40387b73b5253
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/custom/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_library(clangTidyCustomModule STATIC
+ CustomTidyModule.cpp
+ QueryCheck.cpp
+
+ LINK_LIBS
+ clangTidy
+ clangTidyUtils
+
+ DEPENDS
+ ClangDriverOptions
+ )
+
+clang_target_link_libraries(clangTidyCustomModule
+ PRIVATE
+ clangQuery
+ )
diff --git a/clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp b/clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp
new file mode 100644
index 0000000000000..e11a39f1a4ccf
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp
@@ -0,0 +1,50 @@
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../ClangTidyOptions.h"
+#include "QueryCheck.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace clang::tidy {
+namespace custom {
+
+class CustomModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {}
+};
+
+// FIXME: could be clearer to add parameter of addCheckFactories to pass
+// Options?
+extern void registerCustomChecks(ClangTidyOptions const &Options,
+ ClangTidyCheckFactories &Factories) {
+ static llvm::SmallSet<llvm::SmallString<32>, 8> CustomCheckNames{};
+ if (!Options.CustomChecks.has_value() || Options.CustomChecks->empty())
+ return;
+ for (llvm::SmallString<32> const &Name : CustomCheckNames)
+ Factories.erase(Name);
+ for (const ClangTidyOptions::CustomCheckValue &V :
+ Options.CustomChecks.value()) {
+ llvm::SmallString<32> Name = llvm::StringRef{"custom-" + V.Name};
+ Factories.registerCheckFactory(
+ // add custom- prefix to avoid conflicts with builtin checks
+ Name, [&V](llvm::StringRef Name, ClangTidyContext *Context) {
+ return std::make_unique<custom::QueryCheck>(Name, V, Context);
+ });
+ CustomCheckNames.insert(std::move(Name));
+ }
+}
+
+} // namespace custom
+
+// Register the AlteraTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<custom::CustomModule>
+ X("custom-module", "Adds custom query lint checks.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the AlteraModule.
+volatile int CustomModuleAnchorSource = 0;
+
+} // namespace clang::tidy
diff --git a/clang-tools-extra/clang-tidy/custom/QueryCheck.cpp b/clang-tools-extra/clang-tidy/custom/QueryCheck.cpp
new file mode 100644
index 0000000000000..c69e76918f7ed
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/custom/QueryCheck.cpp
@@ -0,0 +1,102 @@
+//===--- QueryCheck.cpp - clang-tidy --------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryCheck.h"
+#include "../../clang-query/Query.h"
+#include "../../clang-query/QueryParser.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::custom {
+
+QueryCheck::QueryCheck(llvm::StringRef Name,
+ const ClangTidyOptions::CustomCheckValue &V,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {
+ for (const ClangTidyOptions::CustomCheckDiag &D : V.Diags) {
+ auto DiagnosticIdIt =
+ Diags
+ .try_emplace(D.Level.value_or(DiagnosticIDs::Warning),
+ llvm::StringMap<llvm::SmallVector<std::string>>{})
+ .first;
+ auto DiagMessageIt =
+ DiagnosticIdIt->getSecond()
+ .try_emplace(D.BindName, llvm::SmallVector<std::string>{})
+ .first;
+ DiagMessageIt->second.emplace_back(D.Message);
+ }
+
+ clang::query::QuerySession QS({});
+ llvm::StringRef QueryStringRef{V.Query};
+ while (!QueryStringRef.empty()) {
+ query::QueryRef Q = query::QueryParser::parse(QueryStringRef, QS);
+ switch (Q->Kind) {
+ case query::QK_Match: {
+ const auto &MatchQuerry = llvm::cast<query::MatchQuery>(*Q);
+ Matchers.push_back(MatchQuerry.Matcher);
+ break;
+ }
+ case query::QK_Let: {
+ const auto &LetQuerry = llvm::cast<query::LetQuery>(*Q);
+ LetQuerry.run(llvm::errs(), QS);
+ break;
+ }
+ case query::QK_Invalid: {
+ const auto &InvalidQuerry = llvm::cast<query::InvalidQuery>(*Q);
+ Context->configurationDiag(InvalidQuerry.ErrStr);
+ break;
+ }
+ // FIXME: TODO
+ case query::QK_File:
+ case query::QK_DisableOutputKind:
+ case query::QK_EnableOutputKind:
+ case query::QK_SetOutputKind:
+ case query::QK_SetTraversalKind:
+ case query::QK_Help:
+ case query::QK_NoOp:
+ case query::QK_Quit:
+ case query::QK_SetBool: {
+ Context->configurationDiag("unsupported querry kind");
+ }
+ }
+ QueryStringRef = Q->RemainingContent;
+ }
+}
+
+void QueryCheck::registerMatchers(MatchFinder *Finder) {
+ for (const ast_matchers::dynamic::DynTypedMatcher &M : Matchers)
+ Finder->addDynamicMatcher(M, this);
+}
+
+void QueryCheck::check(const MatchFinder::MatchResult &Result) {
+ auto Emit = [this](DiagMaps const &DiagMaps, std::string const &BindName,
+ DynTypedNode const &Node, DiagnosticIDs::Level Level) {
+ if (!DiagMaps.contains(Level))
+ return;
+ auto &DiagMap = DiagMaps.at(Level);
+ if (!DiagMap.contains(BindName))
+ return;
+ for (const std::string &Message : DiagMap.at(BindName)) {
+ diag(Node.getSourceRange().getBegin(), Message, Level);
+ }
+ };
+ for (auto &[Name, Node] : Result.Nodes.getMap())
+ Emit(Diags, Name, Node, DiagnosticIDs::Error);
+ for (auto &[Name, Node] : Result.Nodes.getMap())
+ Emit(Diags, Name, Node, DiagnosticIDs::Warning);
+ // place Note last, otherwise it will not be emitted
+ for (auto &[Name, Node] : Result.Nodes.getMap())
+ Emit(Diags, Name, Node, DiagnosticIDs::Note);
+}
+} // namespace clang::tidy::custom
diff --git a/clang-tools-extra/clang-tidy/custom/QueryCheck.h b/clang-tools-extra/clang-tidy/custom/QueryCheck.h
new file mode 100644
index 0000000000000..ded4cad4e3459
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/custom/QueryCheck.h
@@ -0,0 +1,42 @@
+//===--- QueryCheck.h - clang-tidy ------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang::tidy::custom {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/custom/query.html
+class QueryCheck : public ClangTidyCheck {
+public:
+ QueryCheck(llvm::StringRef Name, const ClangTidyOptions::CustomCheckValue &V,
+ ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ llvm::SmallVector<ast_matchers::dynamic::DynTypedMatcher> Matchers{};
+ using DiagMaps =
+ llvm::DenseMap<DiagnosticIDs::Level,
+ llvm::StringMap<llvm::SmallVector<std::string>>>;
+ DiagMaps Diags;
+};
+
+} // namespace clang::tidy::custom
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index fa8887e4639b4..5784b05d2281d 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -60,6 +60,8 @@ Configuration files:
Checks - Same as '--checks'. Additionally, the list of
globs can be specified as a list instead of a
string.
+ CustomChecks - Array of user defined checks based on
+ clang-query syntax.
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
ExtraArgs - Same as '--extra-arg'.
ExtraArgsBefore - Same as '--extra-arg-before'.
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 2252efb498c2c..6d22f83f2248b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -96,6 +96,9 @@ Improvements to clang-tidy
`SystemHeaders` option is enabled.
Note: this may lead to false negatives; downstream users may need to adjust
their checks to preserve existing behavior.
+- :program:`clang-tidy` now supports query based custom checks by `CustomChecks`
+ configuration option.
+ :doc:`Query Based Custom Check Document <clang-tidy/QueryBasedCustomChecks>`
New checks
^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst b/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst
new file mode 100644
index 0000000000000..6efd8fe6797df
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst
@@ -0,0 +1,57 @@
+====================================
+Query Based Custom Clang-Tidy Checks
+====================================
+
+Introduction
+============
+
+This page provides examples of how to add query based custom checks for
+:program:`clang-tidy`.
+
+Custom checks are based on clang-query syntax. Every custom checks will be
+registered in `custom` module to avoid name conflict. They can be enabled or
+disabled by the checks option like the builtin checks.
+
+Custom checks support inheritance from parent configurations like other
+configuration items.
+
+Configuration
+=============
+
+`CustomChecks` is a list of custom checks. Each check must contain
+ - Name: check name can been used in `-checks` option.
+ - Query: query string
+ - Diagnostic: list of diagnostics to be reported.
+ - BindName: name of the node to be bound in `Query`.
+ - Message: message to be reported.
+ - Level: severity of the diagnostic, the possible val...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/131804
More information about the cfe-commits
mailing list