https://github.com/ArcsinX updated https://github.com/llvm/llvm-project/pull/154836
>From 27b44cefdc1ea4b373f8e990bc1fea05fc278701 Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <platonov.aleksa...@huawei.com> Date: Thu, 21 Aug 2025 22:51:41 +0300 Subject: [PATCH 1/2] [clangd] Introduce feature modules registry This patch adds feature modules registry, which allows to discover registered feature modules and add them into the clangd's feature module set --- clang-tools-extra/clangd/FeatureModule.cpp | 15 +++++++++++++++ clang-tools-extra/clangd/FeatureModule.h | 17 ++++++++++++++--- clang-tools-extra/clangd/tool/ClangdMain.cpp | 5 +++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/FeatureModule.cpp b/clang-tools-extra/clangd/FeatureModule.cpp index 872cea1443789..38461e3db4e6b 100644 --- a/clang-tools-extra/clangd/FeatureModule.cpp +++ b/clang-tools-extra/clangd/FeatureModule.cpp @@ -22,6 +22,10 @@ FeatureModule::Facilities &FeatureModule::facilities() { return *Fac; } +void FeatureModuleSet::add(std::unique_ptr<FeatureModule> M) { + Modules.push_back(std::move(M)); +} + bool FeatureModuleSet::addImpl(void *Key, std::unique_ptr<FeatureModule> M, const char *Source) { if (!Map.try_emplace(Key, M.get()).second) { @@ -33,5 +37,16 @@ bool FeatureModuleSet::addImpl(void *Key, std::unique_ptr<FeatureModule> M, return true; } +FeatureModuleSet FeatureModuleSet::fromRegistry() { + FeatureModuleSet ModuleSet; + for (FeatureModuleRegistry::entry E : FeatureModuleRegistry::entries()) { + vlog("Adding feature module '{0}' ({1})", E.getName(), E.getDesc()); + ModuleSet.add(E.instantiate()); + } + return ModuleSet; +} + } // namespace clangd } // namespace clang + +LLVM_INSTANTIATE_REGISTRY(clang::clangd::FeatureModuleRegistry) diff --git a/clang-tools-extra/clangd/FeatureModule.h b/clang-tools-extra/clangd/FeatureModule.h index 7b6883507be3f..ee65aa8a59ed2 100644 --- a/clang-tools-extra/clangd/FeatureModule.h +++ b/clang-tools-extra/clangd/FeatureModule.h @@ -15,6 +15,7 @@ #include "llvm/ADT/FunctionExtras.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/Registry.h" #include <memory> #include <optional> #include <type_traits> @@ -143,9 +144,14 @@ class FeatureModule { /// A FeatureModuleSet is a collection of feature modules installed in clangd. /// -/// Modules can be looked up by type, or used via the FeatureModule interface. -/// This allows individual modules to expose a public API. -/// For this reason, there can be only one feature module of each type. +/// Modules added with explicit type specification can be looked up by type, or +/// used via the FeatureModule interface. This allows individual modules to +/// expose a public API. For this reason, there can be only one feature module +/// of each type. +/// +/// Modules added using a base class pointer can be used only via the +/// FeatureModule interface and can't be looked up by type, thus custom public +/// API (if provided by the module) can't be used. /// /// The set owns the modules. It is itself owned by main, not ClangdServer. class FeatureModuleSet { @@ -164,6 +170,8 @@ class FeatureModuleSet { public: FeatureModuleSet() = default; + static FeatureModuleSet fromRegistry(); + using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>; using const_iterator = llvm::pointee_iterator<decltype(Modules)::const_iterator>; @@ -172,6 +180,7 @@ class FeatureModuleSet { const_iterator begin() const { return const_iterator(Modules.begin()); } const_iterator end() const { return const_iterator(Modules.end()); } + void add(std::unique_ptr<FeatureModule> M); template <typename Mod> bool add(std::unique_ptr<Mod> M) { return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION); } @@ -185,6 +194,8 @@ class FeatureModuleSet { template <typename Mod> int FeatureModuleSet::ID<Mod>::Key; +using FeatureModuleRegistry = llvm::Registry<FeatureModule>; + } // namespace clangd } // namespace clang #endif diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index f287439f10cab..1856d4f47ffc5 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -13,6 +13,7 @@ #include "Config.h" #include "ConfigProvider.h" #include "Feature.h" +#include "FeatureModule.h" #include "IncludeCleaner.h" #include "PathMapping.h" #include "Protocol.h" @@ -1017,6 +1018,10 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var : static_cast<int>(ErrorResultCode::CheckFailed); } + FeatureModuleSet ModuleSet = FeatureModuleSet::fromRegistry(); + if (ModuleSet.begin() != ModuleSet.end()) + Opts.FeatureModules = &ModuleSet; + // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows. llvm::sys::ChangeStdinToBinary(); >From c0961a4094a355539989fc4ed54bdfda1fb3047c Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <platonov.aleksa...@huawei.com> Date: Wed, 27 Aug 2025 23:23:06 +0300 Subject: [PATCH 2/2] Add feature modules registry test --- clang-tools-extra/clangd/FeatureModule.h | 5 ++ .../clangd/unittests/CMakeLists.txt | 5 ++ .../unittests/FeatureModulesRegistryTests.cpp | 66 +++++++++++++++++++ .../unittests/feature-modules/CMakeLists.txt | 2 + .../unittests/feature-modules/ForceLinker.h | 20 ++++++ .../feature-modules/dummy/CMakeLists.txt | 2 + .../dummy/DummyFeatureModule.cpp | 41 ++++++++++++ 7 files changed, 141 insertions(+) create mode 100644 clang-tools-extra/clangd/unittests/FeatureModulesRegistryTests.cpp create mode 100644 clang-tools-extra/clangd/unittests/feature-modules/CMakeLists.txt create mode 100644 clang-tools-extra/clangd/unittests/feature-modules/ForceLinker.h create mode 100644 clang-tools-extra/clangd/unittests/feature-modules/dummy/CMakeLists.txt create mode 100644 clang-tools-extra/clangd/unittests/feature-modules/dummy/DummyFeatureModule.cpp diff --git a/clang-tools-extra/clangd/FeatureModule.h b/clang-tools-extra/clangd/FeatureModule.h index ee65aa8a59ed2..55ca908a6ec51 100644 --- a/clang-tools-extra/clangd/FeatureModule.h +++ b/clang-tools-extra/clangd/FeatureModule.h @@ -198,4 +198,9 @@ using FeatureModuleRegistry = llvm::Registry<FeatureModule>; } // namespace clangd } // namespace clang + +namespace llvm { +extern template class Registry<clang::clangd::FeatureModule>; +} // namespace llvm + #endif diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 9656eeaeb37ce..9dd5f8b3b9204 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -54,6 +54,7 @@ add_unittest(ClangdUnitTests ClangdTests DumpASTTests.cpp ExpectedTypeTest.cpp FeatureModulesTests.cpp + FeatureModulesRegistryTests.cpp FileDistanceTests.cpp FileIndexTests.cpp FindSymbolsTests.cpp @@ -154,6 +155,8 @@ target_include_directories(ClangdTests PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> ) +add_subdirectory(feature-modules) + clang_target_link_libraries(ClangdTests PRIVATE clangAST @@ -183,6 +186,8 @@ target_link_libraries(ClangdTests clangTidy clangTidyUtils clangdSupport + + ${FEATURE_MODULES} ) if (CLANGD_ENABLE_REMOTE) diff --git a/clang-tools-extra/clangd/unittests/FeatureModulesRegistryTests.cpp b/clang-tools-extra/clangd/unittests/FeatureModulesRegistryTests.cpp new file mode 100644 index 0000000000000..0495897a8079f --- /dev/null +++ b/clang-tools-extra/clangd/unittests/FeatureModulesRegistryTests.cpp @@ -0,0 +1,66 @@ +//===--- FeatureModulesRegistryTests.cpp ---------------------------------===// +// +// 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 "FeatureModule.h" +#include "feature-modules/ForceLinker.h" // IWYU pragma: keep +#include "refactor/Tweak.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::ElementsAre; + +namespace llvm { +raw_ostream &operator<<(raw_ostream &OS, + const clang::clangd::FeatureModuleRegistry::entry &E) { + OS << "(name = " << E.getName() << ", description = '" << E.getDesc() << "')"; + return OS; +} + +raw_ostream &operator<<( + raw_ostream &OS, + const iterator_range<Registry<clang::clangd::FeatureModule>::iterator> + &Rng) { + OS << "{ "; + bool First = true; + for (clang::clangd::FeatureModuleRegistry::entry E : Rng) { + if (First) + First = false; + else + OS << ", "; + OS << E; + } + OS << " }"; + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const clang::clangd::Tweak &T) { + OS << "(id = " << T.id() << ", " + << "title = " << T.title() << ")"; + return OS; +} +} // namespace llvm + +namespace clang::clangd { +namespace { + +MATCHER_P(moduleName, Name, "") { return arg.getName() == Name; } +MATCHER_P(tweakID, ID, "") { return arg->id() == ID; } + +TEST(FeatureModulesRegistryTest, DummyModule) { + EXPECT_THAT(FeatureModuleRegistry::entries(), + ElementsAre(moduleName("dummy"))); + FeatureModuleSet Set = FeatureModuleSet::fromRegistry(); + ASSERT_EQ(Set.end() - Set.begin(), 1u); + std::vector<std::unique_ptr<Tweak>> Tweaks; + Set.begin()->contributeTweaks(Tweaks); + EXPECT_THAT(Tweaks, ElementsAre(tweakID("DummyTweak"))); +} + +} // namespace +} // namespace clang::clangd diff --git a/clang-tools-extra/clangd/unittests/feature-modules/CMakeLists.txt b/clang-tools-extra/clangd/unittests/feature-modules/CMakeLists.txt new file mode 100644 index 0000000000000..e7a3757d56447 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/feature-modules/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(dummy) +set(FEATURE_MODULES ${FEATURE_MODULES} PARENT_SCOPE) diff --git a/clang-tools-extra/clangd/unittests/feature-modules/ForceLinker.h b/clang-tools-extra/clangd/unittests/feature-modules/ForceLinker.h new file mode 100644 index 0000000000000..85bc0b7638a37 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/feature-modules/ForceLinker.h @@ -0,0 +1,20 @@ +//===- ForceLinker.h ----------------------------------------------*-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_CLANGD_UNITTEST_FEATURE_MODULES_FORCE_LINKER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTEST_FEATURE_MODULES_FORCE_LINKER_H + +#include "llvm/Support/Compiler.h" + +namespace clang::clangd { +extern volatile int DummyFeatureModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED DummyFeatureModuleAnchorDestination = + DummyFeatureModuleAnchorSource; +} // namespace clang::clangd + +#endif diff --git a/clang-tools-extra/clangd/unittests/feature-modules/dummy/CMakeLists.txt b/clang-tools-extra/clangd/unittests/feature-modules/dummy/CMakeLists.txt new file mode 100644 index 0000000000000..7a4a8de44bca7 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/feature-modules/dummy/CMakeLists.txt @@ -0,0 +1,2 @@ +add_llvm_library(dummyFeatureModule DummyFeatureModule.cpp) +set(FEATURE_MODULES ${FEATURE_MODULES} dummyFeatureModule PARENT_SCOPE) diff --git a/clang-tools-extra/clangd/unittests/feature-modules/dummy/DummyFeatureModule.cpp b/clang-tools-extra/clangd/unittests/feature-modules/dummy/DummyFeatureModule.cpp new file mode 100644 index 0000000000000..f1e65f46d0bac --- /dev/null +++ b/clang-tools-extra/clangd/unittests/feature-modules/dummy/DummyFeatureModule.cpp @@ -0,0 +1,41 @@ +//===--- DummyFeatureModule.cpp -------------------------------------------===// +// +// 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 "FeatureModule.h" +#include "refactor/Tweak.h" +#include "support/Logger.h" + +namespace clang::clangd { + +class Dummy final : public FeatureModule { + static constexpr const char *TweakID = "DummyTweak"; + struct DummyTweak final : public Tweak { + const char *id() const override { return TweakID; } + bool prepare(const Selection &) override { return true; } + Expected<Effect> apply(const Selection &) override { + return error("not implemented"); + } + std::string title() const override { return id(); } + llvm::StringLiteral kind() const override { + return llvm::StringLiteral(""); + }; + }; + + void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override { + Out.emplace_back(new DummyTweak); + } +}; + +static FeatureModuleRegistry::Add<Dummy> + X("dummy", "Dummy feature module with dummy tweak"); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the Dummy feature module. +volatile int DummyFeatureModuleAnchorSource = 0; + +} // namespace clang::clangd _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits