https://github.com/ArcsinX updated https://github.com/llvm/llvm-project/pull/226995
>From b8fbd5d0c189f9cfa1e7272407afbba87f82fb97 Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <[email protected]> Date: Thu, 24 Sep 2026 23:33:25 +0300 Subject: [PATCH] [clangd][FeatureModule] Allow dynamic loading --- clang-tools-extra/clangd/test/CMakeLists.txt | 30 +++++++++++++ .../clangd/test/FeatureModuleExample.cpp | 42 +++++++++++++++++++ .../clangd/test/StandaloneTweakExample.cpp | 32 ++++++++++++++ .../clangd/test/feature-module.test | 14 +++++++ clang-tools-extra/clangd/test/lit.cfg.py | 5 +++ .../clangd/test/lit.site.cfg.py.in | 2 + .../clangd/test/standalone-tweak.test | 10 +++++ clang-tools-extra/clangd/tool/CMakeLists.txt | 10 +++++ clang-tools-extra/clangd/tool/ClangdMain.cpp | 16 +++++-- clang-tools-extra/docs/ReleaseNotes.md | 3 ++ 10 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 clang-tools-extra/clangd/test/FeatureModuleExample.cpp create mode 100644 clang-tools-extra/clangd/test/StandaloneTweakExample.cpp create mode 100644 clang-tools-extra/clangd/test/feature-module.test create mode 100644 clang-tools-extra/clangd/test/standalone-tweak.test diff --git a/clang-tools-extra/clangd/test/CMakeLists.txt b/clang-tools-extra/clangd/test/CMakeLists.txt index 608835cab2fc5e6..401d3a8b156db71 100644 --- a/clang-tools-extra/clangd/test/CMakeLists.txt +++ b/clang-tools-extra/clangd/test/CMakeLists.txt @@ -30,6 +30,36 @@ foreach(dep FileCheck count not llvm-config) endif() endforeach() +if (NOT WIN32 OR NOT LLVM_LINK_LLVM_DYLIB) + llvm_add_library( + ClangdFeatureModuleExample + MODULE FeatureModuleExample.cpp + PLUGIN_TOOL clangd + PARTIAL_SOURCES_INTENDED) + llvm_add_library( + ClangdStandaloneTweakExample + MODULE StandaloneTweakExample.cpp + PLUGIN_TOOL clangd + PARTIAL_SOURCES_INTENDED) +endif() + +foreach(target ClangdFeatureModuleExample ClangdStandaloneTweakExample) + if(TARGET ${target}) + list(APPEND CLANGD_TEST_DEPS ${target}) + target_include_directories(${target} PUBLIC BEFORE + "${CLANG_TOOLS_SOURCE_DIR}/clangd" + "${CMAKE_CURRENT_BINARY_DIR}/.." + ) + if(CLANG_PLUGIN_SUPPORT AND (WIN32 OR CYGWIN)) + set(LLVM_LINK_COMPONENTS + Support + ) + endif() + endif() +endforeach() + +llvm_canonicalize_cmake_booleans(CLANG_PLUGIN_SUPPORT) + configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py diff --git a/clang-tools-extra/clangd/test/FeatureModuleExample.cpp b/clang-tools-extra/clangd/test/FeatureModuleExample.cpp new file mode 100644 index 000000000000000..e1e86678f30f538 --- /dev/null +++ b/clang-tools-extra/clangd/test/FeatureModuleExample.cpp @@ -0,0 +1,42 @@ +//===--- FeatureModuleExample.cpp - Example dynamic FeatureModule ---------===// +// +// 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 { +namespace { + +/// An example tweak contributed by the dynamic feature module. +class ExampleModuleTweak final : public Tweak { +public: + const char *id() const override { return "ExampleModuleTweak"; } + bool prepare(const Selection &) override { return true; } + Expected<Effect> apply(const Selection &) override { + return Effect::showMessage("Example module tweak executed"); + } + std::string title() const override { return "Execute example module action"; } + llvm::StringLiteral kind() const override { + return llvm::StringLiteral("quickfix"); + } +}; + +/// An example FeatureModule loaded dynamically into clangd. +class ExampleFeatureModule final : public FeatureModule { +public: + void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override { + Out.emplace_back(new ExampleModuleTweak); + } +}; + +static FeatureModuleRegistry::Add<ExampleFeatureModule> + X("example-feature-module", "Example clangd feature module"); + +} // namespace +} // namespace clang::clangd diff --git a/clang-tools-extra/clangd/test/StandaloneTweakExample.cpp b/clang-tools-extra/clangd/test/StandaloneTweakExample.cpp new file mode 100644 index 000000000000000..3afe027fd7ab647 --- /dev/null +++ b/clang-tools-extra/clangd/test/StandaloneTweakExample.cpp @@ -0,0 +1,32 @@ +//===--- StandaloneTweakExample.cpp - Example standalone dynamic Tweak ----===// +// +// 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 "refactor/Tweak.h" + +namespace clang::clangd { +namespace { + +/// A standalone tweak registered directly via REGISTER_TWEAK without a +/// FeatureModule. +class StandaloneTweak final : public Tweak { +public: + const char *id() const override; + bool prepare(const Selection &) override { return true; } + Expected<Effect> apply(const Selection &) override { + return Effect::showMessage("Standalone tweak executed"); + } + std::string title() const override { return "Execute standalone action"; } + llvm::StringLiteral kind() const override { + return llvm::StringLiteral("quickfix"); + } +}; + +REGISTER_TWEAK(StandaloneTweak) + +} // namespace +} // namespace clang::clangd diff --git a/clang-tools-extra/clangd/test/feature-module.test b/clang-tools-extra/clangd/test/feature-module.test new file mode 100644 index 000000000000000..e25cf8933f6ff82 --- /dev/null +++ b/clang-tools-extra/clangd/test/feature-module.test @@ -0,0 +1,14 @@ +// REQUIRES: plugins +// RUN: clangd --help | FileCheck --check-prefix=CHECK-HELP %s +// CHECK-HELP: --load=<pluginfilename> - Load the specified plugin + +// RUN: cp %s %t.cpp +// RUN: clangd -enable-config=0 -log=verbose -check=%t.cpp -load=%llvmshlibdir/ClangdFeatureModuleExample%pluginext 2>&1 | FileCheck -strict-whitespace %s + +// CHECK: Loaded plugin: {{.*}}ClangdFeatureModuleExample{{.*}} +// CHECK: Adding feature module 'example-feature-module' (Example clangd feature module) +// CHECK: Testing features at each token +// CHECK-DAG: tweak: ExampleModuleTweak +// CHECK: All checks completed, 0 errors + +int x = 42; diff --git a/clang-tools-extra/clangd/test/lit.cfg.py b/clang-tools-extra/clangd/test/lit.cfg.py index aac3a938353e8c5..f56a73b56a9113a 100644 --- a/clang-tools-extra/clangd/test/lit.cfg.py +++ b/clang-tools-extra/clangd/test/lit.cfg.py @@ -43,6 +43,11 @@ def calculate_arch_features(arch_string): if lit.util.pythonize_bool(config.have_benchmarks): config.available_features.add("have-benchmarks") +# Plugins (loadable modules) +if config.has_plugins and config.llvm_plugin_ext: + config.available_features.add("plugins") + config.substitutions.append(("%pluginext", config.llvm_plugin_ext)) + # It is not realistically possible to account for all options that could # possibly be present in system and user configuration files, so disable # default configs for the test runs. diff --git a/clang-tools-extra/clangd/test/lit.site.cfg.py.in b/clang-tools-extra/clangd/test/lit.site.cfg.py.in index f5ae3eb1f0743d5..650006b8b72f57f 100644 --- a/clang-tools-extra/clangd/test/lit.site.cfg.py.in +++ b/clang-tools-extra/clangd/test/lit.site.cfg.py.in @@ -20,6 +20,8 @@ config.clangd_enable_remote = @CLANGD_ENABLE_REMOTE@ config.clangd_tidy_checks = @CLANGD_TIDY_CHECKS@ config.have_zlib = @LLVM_ENABLE_ZLIB@ config.have_benchmarks = "@LLVM_INCLUDE_BENCHMARKS@" +config.has_plugins = @CLANG_PLUGIN_SUPPORT@ +config.llvm_plugin_ext = "@LLVM_PLUGIN_EXT@" # Delegate logic to lit.cfg.py. lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg.py") diff --git a/clang-tools-extra/clangd/test/standalone-tweak.test b/clang-tools-extra/clangd/test/standalone-tweak.test new file mode 100644 index 000000000000000..c74e81a4a68b3c8 --- /dev/null +++ b/clang-tools-extra/clangd/test/standalone-tweak.test @@ -0,0 +1,10 @@ +// REQUIRES: plugins +// RUN: cp %s %t.cpp +// RUN: clangd -enable-config=0 -log=verbose -check=%t.cpp -load=%llvmshlibdir/ClangdStandaloneTweakExample%pluginext 2>&1 | FileCheck -strict-whitespace %s + +// CHECK: Loaded plugin: {{.*}}ClangdStandaloneTweakExample{{.*}} +// CHECK: Testing features at each token +// CHECK-DAG: tweak: StandaloneTweak +// CHECK: All checks completed, 0 errors + +int x = 42; diff --git a/clang-tools-extra/clangd/tool/CMakeLists.txt b/clang-tools-extra/clangd/tool/CMakeLists.txt index 1bd6a1f864dd3b0..bae511341219f07 100644 --- a/clang-tools-extra/clangd/tool/CMakeLists.txt +++ b/clang-tools-extra/clangd/tool/CMakeLists.txt @@ -9,9 +9,15 @@ add_clang_library(clangdMain STATIC DISABLE_PCH_REUSE ) +# Support plugins. +if(CLANG_PLUGIN_SUPPORT) + set(support_plugins SUPPORT_PLUGINS) +endif() + add_clang_tool(clangd ClangdToolMain.cpp $<TARGET_OBJECTS:obj.clangDaemonTweaks> + ${support_plugins} ) set(LLVM_LINK_COMPONENTS @@ -61,3 +67,7 @@ target_link_libraries(clangd clangDaemon clangdSupport ) + +if(CLANG_PLUGIN_SUPPORT) + export_executable_symbols_for_plugins(clangd) +endif() diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 9dffa3bef8caa2b..25c987d9c293418 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" +#include "llvm/Support/PluginLoader.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" @@ -788,6 +789,11 @@ It should be used via an editor plugin rather than invoked directly. For more in clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable. )"; + // Enable help for -load option, if plugins are enabled. + if (llvm::cl::Option *LoadOpt = + llvm::cl::getRegisteredOptions().lookup("load")) + LoadOpt->addCategory(Features); + llvm::cl::HideUnrelatedOptions(ClangdCategories); llvm::cl::ParseCommandLineOptions(argc, argv, Overview, /*Errs=*/nullptr, /*VFS=*/nullptr, FlagsEnvVar); @@ -907,6 +913,8 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var } for (int I = 0; I < argc; ++I) log("argv[{0}]: {1}", I, argv[I]); + for (unsigned I = 0, E = llvm::PluginLoader::getNumPlugins(); I < E; ++I) + log("Loaded plugin: {0}", llvm::PluginLoader::getPlugin(I)); if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar)) log("{0}: {1}", FlagsEnvVar, *EnvFlags); // Log environment variables that influence how clangd finds system headers. @@ -1028,6 +1036,10 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding) Opts.Encoding = ForceOffsetEncoding; + FeatureModuleSet ModuleSet = FeatureModuleSet::fromRegistry(); + if (ModuleSet.begin() != ModuleSet.end()) + Opts.FeatureModules = &ModuleSet; + if (CheckFile.getNumOccurrences()) { llvm::SmallString<256> Path; if (auto Error = @@ -1041,10 +1053,6 @@ 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(); diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index 42451c459059284..a32d3fbee3f9603 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -116,6 +116,9 @@ infrastructure are described first, followed by tool-specific sections. user config) by default. Pass `--enable-config=false` to restore the previous behavior. +- Added support for loading dynamic plugins via the `-load` command-line + option. + ### Improvements to clang-doc ### Improvements to clang-query _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
