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

Reply via email to