https://github.com/voyager-jhk created https://github.com/llvm/llvm-project/pull/216336
Enable query-based custom checks in clangd behind a new `Diagnostics.ClangTidy.ExperimentalCustomChecks` configuration option. Custom checks are registered from the per-file clang-tidy options and remain subject to clangd's `FastCheckFilter` policy. Fixes #206696 Assisted by GPT-5.6 >From 7cd17d82b4e229a125ddd55c65735e734042b4b5 Mon Sep 17 00:00:00 2001 From: voyager-jhk <[email protected]> Date: Fri, 14 Aug 2026 22:37:24 +0800 Subject: [PATCH] [clangd] Enable query-based custom checks Enable query-based custom checks in clangd behind a new `Diagnostics.ClangTidy.ExperimentalCustomChecks` configuration option. Custom checks are registered from the per-file clang-tidy options and remain subject to clangd's `FastCheckFilter` policy. Fixes #206696 --- clang-tools-extra/clangd/Config.h | 1 + clang-tools-extra/clangd/ConfigCompile.cpp | 5 +++ clang-tools-extra/clangd/ConfigFragment.h | 4 ++ clang-tools-extra/clangd/ConfigYAML.cpp | 4 ++ clang-tools-extra/clangd/ParsedAST.cpp | 23 ++++++++-- .../clangd/unittests/ConfigCompileTests.cpp | 14 ++++++ .../clangd/unittests/ConfigYAMLTests.cpp | 15 +++++++ .../clangd/unittests/DiagnosticsTests.cpp | 43 +++++++++++++++++++ clang-tools-extra/docs/ReleaseNotes.md | 27 +++++++----- .../clang-tidy/QueryBasedCustomChecks.rst | 19 ++++++++ 10 files changed, 142 insertions(+), 13 deletions(-) diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index a94a1727199bd..7365c59bb1b36 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -111,6 +111,7 @@ struct Config { std::string Checks; llvm::StringMap<std::string> CheckOptions; FastCheckPolicy FastCheckFilter = FastCheckPolicy::Strict; + bool ExperimentalCustomChecks = false; } ClangTidy; IncludesPolicy UnusedIncludes = IncludesPolicy::Strict; diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index 2b41949d6d05c..34394453ca8a4 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -624,6 +624,11 @@ struct FragmentCompiler { Out.Apply.push_back([Val](const Params &, Config &C) { C.Diagnostics.ClangTidy.FastCheckFilter = *Val; }); + if (F.ExperimentalCustomChecks) + Out.Apply.push_back( + [Enabled = **F.ExperimentalCustomChecks](const Params &, Config &C) { + C.Diagnostics.ClangTidy.ExperimentalCustomChecks = Enabled; + }); } void compile(Fragment::DiagnosticsBlock::IncludesBlock &&F) { diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index 7604fe4e24c97..560c8d0ed2cf3 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -297,6 +297,10 @@ struct Fragment { /// Loose: Run checks unless they are known to be slow. /// None: Run checks regardless of their speed. std::optional<Located<std::string>> FastCheckFilter; + + /// Whether to enable experimental query-based custom checks configured + /// in .clang-tidy files. + std::optional<Located<bool>> ExperimentalCustomChecks; }; ClangTidyBlock ClangTidy; }; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index 940c7b8bd2de1..ad1bd814f8405 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -174,6 +174,10 @@ class Parser { if (auto FastCheckFilter = scalarValue(N, "FastCheckFilter")) F.FastCheckFilter = *FastCheckFilter; }); + Dict.handle("ExperimentalCustomChecks", [&](Node &N) { + if (auto Value = boolValue(N, "ExperimentalCustomChecks")) + F.ExperimentalCustomChecks = *Value; + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index df56420cd7f24..b498f8535abf6 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ParsedAST.h" +#include "../clang-tidy/ClangTidy.h" #include "../clang-tidy/ClangTidyCheck.h" #include "../clang-tidy/ClangTidyDiagnosticConsumer.h" #include "../clang-tidy/ClangTidyModule.h" @@ -69,6 +70,7 @@ #include <cstddef> #include <iterator> #include <memory> +#include <mutex> #include <optional> #include <string> #include <tuple> @@ -574,15 +576,30 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, E.instantiate()->addCheckFactories(*CTFactories); return CTFactories; }(); - tidy::ClangTidyCheckFactories FastFactories = filterFastTidyChecks( - *AllCTFactories, Cfg.Diagnostics.ClangTidy.FastCheckFilter); CTContext.emplace(std::make_unique<tidy::DefaultOptionsProvider>( - tidy::ClangTidyGlobalOptions(), ClangTidyOpts)); + tidy::ClangTidyGlobalOptions(), ClangTidyOpts), + /*AllowEnablingAnalyzerAlphaCheckers=*/false, + /*EnableModuleHeadersParsing=*/false, + Cfg.Diagnostics.ClangTidy.ExperimentalCustomChecks); // The lifetime of DiagnosticOptions is managed by \c Clang. CTContext->setDiagnosticsEngine(nullptr, &Clang->getDiagnostics()); CTContext->setASTContext(&Clang->getASTContext()); CTContext->setCurrentFile(Filename); CTContext->setSelfContainedDiags(true); + tidy::ClangTidyCheckFactories CTCheckFactories = *AllCTFactories; +#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS + if (CTContext->canExperimentalCustomChecks() && + tidy::custom::RegisterCustomChecks) { + // RegisterCustomChecks tracks names in process-wide mutable state. + // Serializing its use keeps concurrent AST builds independent. + static std::mutex CustomChecksMu; + std::lock_guard<std::mutex> Lock(CustomChecksMu); + tidy::custom::RegisterCustomChecks(CTContext->getOptions(), + CTCheckFactories); + } +#endif + tidy::ClangTidyCheckFactories FastFactories = filterFastTidyChecks( + CTCheckFactories, Cfg.Diagnostics.ClangTidy.FastCheckFilter); CTChecks = FastFactories.createChecksForLanguage(&*CTContext); Preprocessor *PP = &Clang->getPreprocessor(); for (const auto &Check : CTChecks) { diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp index 5fecf32f8e5a5..6759afa79bbcd 100644 --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -333,6 +333,20 @@ TEST_F(ConfigCompileTests, Tidy) { #endif } +TEST_F(ConfigCompileTests, TidyExperimentalCustomChecks) { + EXPECT_FALSE(Conf.Diagnostics.ClangTidy.ExperimentalCustomChecks); + + Frag.Diagnostics.ClangTidy.ExperimentalCustomChecks = true; + EXPECT_TRUE(compileAndApply()); + EXPECT_TRUE(Conf.Diagnostics.ClangTidy.ExperimentalCustomChecks); + + Fragment Override; + Override.Diagnostics.ClangTidy.ExperimentalCustomChecks = false; + auto Compiled = std::move(Override).compile(Diags.callback()); + EXPECT_TRUE(Compiled(Parm, Conf)); + EXPECT_FALSE(Conf.Diagnostics.ClangTidy.ExperimentalCustomChecks); +} + TEST_F(ConfigCompileTests, TidyBadChecks) { auto &Tidy = Frag.Diagnostics.ClangTidy; Tidy.Add.emplace_back("unknown-check"); diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp index 264cb453b413c..753cd14eaa32c 100644 --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -320,6 +320,21 @@ TEST(ParseYAML, IncludesAnalyzeAngledIncludes) { llvm::ValueIs(val(true))); } +TEST(ParseYAML, ClangTidyExperimentalCustomChecks) { + CapturedDiags Diags; + Annotations YAML(R"yaml( +Diagnostics: + ClangTidy: + ExperimentalCustomChecks: true + )yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + ASSERT_THAT(Diags.Diagnostics, IsEmpty()); + ASSERT_EQ(Results.size(), 1u); + EXPECT_THAT(Results[0].Diagnostics.ClangTidy.ExperimentalCustomChecks, + llvm::ValueIs(val(true))); +} + TEST(ParseYAML, Style) { CapturedDiags Diags; Annotations YAML(R"yaml( diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 6d91ac1ef1e8e..ed743f072822b 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -18,6 +18,7 @@ #include "TestIndex.h" #include "TestTU.h" #include "TidyProvider.h" +#include "clang-tidy-config.h" #include "index/MemIndex.h" #include "index/Ref.h" #include "index/Relation.h" @@ -359,6 +360,48 @@ TEST(DiagnosticsTest, ClangTidy) { "function 'bar' is within a recursive call chain")))); } +#if CLANGD_TIDY_CHECKS && CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS +TEST(DiagnosticsTest, ClangTidyExperimentalCustomChecks) { + Annotations Test(R"cpp( + int x = $literal[[0]]; + )cpp"); + auto TU = TestTU::withCode(Test.code()); + TU.ClangTidyProvider = [](tidy::ClangTidyOptions &Opts, llvm::StringRef) { + Opts.Checks = "-*,custom-integer-literal"; + tidy::ClangTidyOptions::CustomCheckValue Check; + Check.Name = "integer-literal"; + Check.Query = R"(match integerLiteral().bind("literal"))"; + tidy::ClangTidyOptions::CustomCheckDiag Diagnostic; + Diagnostic.BindName = "literal"; + Diagnostic.Message = "integer literal found"; + Diagnostic.Level = DiagnosticIDs::Warning; + Check.Diags.push_back(std::move(Diagnostic)); + Opts.CustomChecks.emplace(); + Opts.CustomChecks->push_back(std::move(Check)); + }; + + auto DiagnosticsFor = [&](bool Enabled, Config::FastCheckPolicy Policy) { + Config Cfg; + Cfg.Diagnostics.ClangTidy.ExperimentalCustomChecks = Enabled; + Cfg.Diagnostics.ClangTidy.FastCheckFilter = Policy; + WithContextValue WithCfg(Config::Key, std::move(Cfg)); + auto AST = TU.build(); + return std::vector<clangd::Diag>(AST.getDiagnostics()); + }; + auto CustomDiagnostic = + AllOf(Diag(Test.range("literal"), "integer literal found"), + diagSource(Diag::ClangTidy), diagName("custom-integer-literal"), + diagSeverity(DiagnosticsEngine::Warning)); + + EXPECT_THAT(DiagnosticsFor(false, Config::FastCheckPolicy::Loose), IsEmpty()); + EXPECT_THAT(DiagnosticsFor(true, Config::FastCheckPolicy::Strict), IsEmpty()); + EXPECT_THAT(DiagnosticsFor(true, Config::FastCheckPolicy::Loose), + ElementsAre(CustomDiagnostic)); + EXPECT_THAT(DiagnosticsFor(true, Config::FastCheckPolicy::None), + ElementsAre(CustomDiagnostic)); +} +#endif + TEST(DiagnosticsTest, ClangTidyRedundantParenthesesFix) { Annotations Test(R"cpp( int func() { diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index 29de9aef9e4b6..d4e3935629b06 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -10,6 +10,7 @@ myst: % ReleaseNotes.md and ReleaseNotesTemplate.txt. {#extra-clang-tools-release-releasenotestitle} + # Extra Clang Tools {{env.config.release}} {{ (('(In-Progress) ' if env.app.tags.has('PreRelease') else '') ~ 'Release Notes') }} ```{contents} @@ -41,11 +42,12 @@ the latest release, please see the [Clang Web Site](https://clang.llvm.org) or the [LLVM Web Site](https://llvm.org). Note that if you are reading this file from a Git checkout or the -main Clang web page, this document applies to the *next* release, not +main Clang web page, this document applies to the _next_ release, not the current one. To see the release notes for a specific release, please see the [releases page](https://llvm.org/releases/). {#what-s-new-in-extra-clang-tools-release} + ## What's New in Extra Clang Tools {{env.config.release}}? Some of the major new features and improvements to Extra Clang Tools are listed @@ -58,7 +60,7 @@ infrastructure are described first, followed by tool-specific sections. - The deprecated `zircon` clang-tidy module has been removed. Users of `zircon-temporary-objects` should migrate to {doc}`fuchsia-temporary-objects - <clang-tidy/checks/fuchsia/temporary-objects>`. +<clang-tidy/checks/fuchsia/temporary-objects>`. - In 22nd release, The `clang-tidy/ClangTidyModuleRegistry.h` header was deprecated. All of the symbols it used to define were moved into `clang-tidy/ClangTidyModule.h`. @@ -70,6 +72,11 @@ infrastructure are described first, followed by tool-specific sections. #### Diagnostics +- Query-based custom clang-tidy checks can now be enabled with the + `Diagnostics.ClangTidy.ExperimentalCustomChecks` clangd configuration option. + Custom checks are subject to `FastCheckFilter`, and therefore require + `FastCheckFilter: Loose` or `None` to run. + #### Semantic Highlighting #### Compile flags @@ -101,7 +108,7 @@ infrastructure are described first, followed by tool-specific sections. #### New checks - New {doc}`performance-expensive-value-or - <clang-tidy/checks/performance/expensive-value-or>` check. +<clang-tidy/checks/performance/expensive-value-or>` check. Finds calls to `value_or` (and alternative spellings `valueOr`, `ValueOr`) on optional types where the return type is expensive to copy. @@ -111,37 +118,37 @@ infrastructure are described first, followed by tool-specific sections. #### Changes in existing checks - Fixed a crash in {doc}`bugprone-misplaced-operator-in-strlen-in-alloc - <clang-tidy/checks/bugprone/misplaced-operator-in-strlen-in-alloc>` when +<clang-tidy/checks/bugprone/misplaced-operator-in-strlen-in-alloc>` when checking an array new expression without a size expression. - Fixed a crash in {doc}`bugprone-std-namespace-modification - <clang-tidy/checks/bugprone/std-namespace-modification>` when checking +<clang-tidy/checks/bugprone/std-namespace-modification>` when checking lambda closure types used as template arguments. - Improved {doc}`cppcoreguidelines-pro-type-member-init - <clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by treating +<clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by treating `std::array` the same as built-in arrays when `IgnoreArrays` option is enabled. - Improved {doc}`misc-redundant-expression - <clang-tidy/checks/misc/redundant-expression>` by fixing false positives in +<clang-tidy/checks/misc/redundant-expression>` by fixing false positives in nested expressions involving different macros or a mix of macro and non-macro operands. - Improved {doc}`modernize-return-braced-init-list - <clang-tidy/checks/modernize/return-braced-init-list>` check to no longer +<clang-tidy/checks/modernize/return-braced-init-list>` check to no longer rewrite the return value when the constructed type has a `std::initializer_list` constructor, as the braced form could select a different constructor. - Improved {doc}`readability-named-parameter - <clang-tidy/checks/readability/named-parameter>` check by ignoring +<clang-tidy/checks/readability/named-parameter>` check by ignoring standard tag types (e.g. `std::in_place_t`, `std::allocator_arg_t`, `std::nothrow_t`, iterator tags, lock tags, etc.) that are used exclusively for overload resolution. Added the {option}`IgnoredTypes` option to allow customizing the set of ignored types. - Improved {doc}`readability-use-std-min-max - <clang-tidy/checks/readability/use-std-min-max>` check by fixing spurious +<clang-tidy/checks/readability/use-std-min-max>` check by fixing spurious trailing semicolons and lost comments when the `if` body has no braces. #### Removed checks diff --git a/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst b/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst index 1d91fad975c7f..73dded0087bdc 100644 --- a/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst +++ b/clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst @@ -63,6 +63,25 @@ Example main(); // warning: call to main function. [custom-call-main-function] } +Using with clangd +================= + +To enable query-based custom checks in clangd, add the following to the clangd +configuration: + +.. code-block:: yaml + + Diagnostics: + ClangTidy: + ExperimentalCustomChecks: true + FastCheckFilter: Loose + +The custom-check definitions and check selection remain part of the clang-tidy +configuration. ``ExperimentalCustomChecks`` does not override clangd's +``FastCheckFilter``. Because custom checks are not present in clangd's +fast-check database, ``Strict`` excludes them, while ``Loose`` and ``None`` +allow them. + Matters Need Attention ====================== _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
