https://github.com/Fil-Den updated https://github.com/llvm/llvm-project/pull/214951
>From 92c6aef642d9cedbee379f0d71e4102c28a35af8 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Sat, 11 Jul 2026 18:46:03 +0300 Subject: [PATCH 01/18] Clangd-indexer respects .clangd files --- clang-tools-extra/clangd/ConfigProvider.cpp | 27 ++++++++++++++ clang-tools-extra/clangd/ConfigProvider.h | 10 +++++ .../clangd/indexer/IndexerMain.cpp | 37 ++++++++++++++++++- .../clangd/test/indexer-clangd-config.test | 25 +++++++++++++ clang-tools-extra/clangd/tool/ClangdMain.cpp | 24 ++---------- 5 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 clang-tools-extra/clangd/test/indexer-clangd-config.test diff --git a/clang-tools-extra/clangd/ConfigProvider.cpp b/clang-tools-extra/clangd/ConfigProvider.cpp index ac437ee8b6eb1..e26509d8b1c8b 100644 --- a/clang-tools-extra/clangd/ConfigProvider.cpp +++ b/clang-tools-extra/clangd/ConfigProvider.cpp @@ -10,10 +10,12 @@ #include "Config.h" #include "ConfigFragment.h" #include "support/FileCache.h" +#include "support/Logger.h" #include "support/Path.h" #include "support/ThreadsafeFS.h" #include "support/Trace.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" @@ -166,6 +168,31 @@ Provider::combine(std::vector<const Provider *> Providers) { return std::make_unique<CombinedProvider>(std::move(Providers)); } +std::vector<std::unique_ptr<Provider>> +Provider::createDefaultProviders(const ThreadsafeFS &TFS) { + std::vector<std::unique_ptr<Provider>> Providers; + Providers.push_back(fromAncestorRelativeYAMLFiles(".clangd", TFS)); + llvm::SmallString<256> UserConfig; + if (llvm::sys::path::user_config_directory(UserConfig)) { + llvm::sys::path::append(UserConfig, "clangd", "config.yaml"); + vlog("User config file is {0}", UserConfig); + Providers.push_back( + fromYAMLFile(UserConfig, /*Directory=*/"", TFS, /*Trusted=*/true)); + } else { + elog("Couldn't determine user config file, not loading"); + } + return Providers; +} + +const char *const Provider::EnableConfigFlagDesc = + "Read user and project configuration from YAML files.\n" + "Project config is from a .clangd file in the project directory.\n" + "User config is from clangd/config.yaml in the following directories:\n" + "\tWindows: %USERPROFILE%\\AppData\\Local\n" + "\tMac OS: ~/Library/Preferences/\n" + "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n" + "Configuration is documented at https://clangd.llvm.org/config.html"; + Config Provider::getConfig(const Params &P, DiagnosticCallback DC) const { trace::Span Tracer("getConfig"); if (!P.Path.empty()) diff --git a/clang-tools-extra/clangd/ConfigProvider.h b/clang-tools-extra/clangd/ConfigProvider.h index f268edb1df2a5..e1a52604e7de8 100644 --- a/clang-tools-extra/clangd/ConfigProvider.h +++ b/clang-tools-extra/clangd/ConfigProvider.h @@ -84,9 +84,19 @@ class Provider { /// Order is preserved; later providers take precedence over earlier ones. static std::unique_ptr<Provider> combine(std::vector<const Provider *>); + /// Returns providers for the configuration files that clangd tools read by + /// default: project config (ancestor `.clangd` files) and the user's + /// global config file. + static std::vector<std::unique_ptr<Provider>> + createDefaultProviders(const ThreadsafeFS &); + /// Build a config based on this provider. Config getConfig(const Params &, DiagnosticCallback) const; + /// Help text for the --enable-config flag, shared by clangd tools that + /// expose createDefaultProviders() on the command line. + static const char *const EnableConfigFlagDesc; + private: /// Provide fragments that may be relevant to the file. /// The configuration provider is not responsible for testing conditions. diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 94db860f0b9b5..c330be298a87a 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -10,21 +10,29 @@ // //===----------------------------------------------------------------------===// +#include "ClangdServer.h" #include "CompileCommands.h" #include "Compiler.h" +#include "ConfigProvider.h" #include "index/IndexAction.h" #include "index/Merge.h" #include "index/Ref.h" #include "index/Serialization.h" #include "index/Symbol.h" #include "index/SymbolCollector.h" +#include "support/Context.h" #include "support/Logger.h" +#include "support/ThreadsafeFS.h" #include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/Execution.h" #include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Signals.h" +#include <memory> #include <utility> +#include <vector> namespace clang { namespace clangd { @@ -48,6 +56,12 @@ static llvm::cl::list<std::string> QueryDriverGlobs{ llvm::cl::CommaSeparated, }; +static llvm::cl::opt<bool> EnableConfig{ + "enable-config", + llvm::cl::desc(config::Provider::EnableConfigFlagDesc), + llvm::cl::init(true), +}; + class IndexActionFactory : public tooling::FrontendActionFactory { public: IndexActionFactory(IndexFileIn &Result) : Result(Result) {} @@ -152,6 +166,20 @@ int main(int argc, const char **argv) { return 1; } + clang::clangd::RealThreadsafeFS TFS; + std::vector<std::unique_ptr<clang::clangd::config::Provider>> ProviderStack; + if (clang::clangd::EnableConfig) + ProviderStack = + clang::clangd::config::Provider::createDefaultProviders(TFS); + std::vector<const clang::clangd::config::Provider *> ProviderPointers; + for (const auto &P : ProviderStack) + ProviderPointers.push_back(P.get()); + auto ConfigProvider = + clang::clangd::config::Provider::combine(std::move(ProviderPointers)); + auto ContextProvider = + clang::clangd::ClangdServer::createConfiguredContextProvider( + ConfigProvider.get(), /*Callbacks=*/nullptr); + // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; auto Mangler = std::make_shared<clang::clangd::CommandMangler>( @@ -162,8 +190,13 @@ int main(int argc, const char **argv) { auto Err = Executor->get()->execute( std::make_unique<clang::clangd::IndexActionFactory>(Data), clang::tooling::ArgumentsAdjuster( - [Mangler = std::move(Mangler)](const std::vector<std::string> &Args, - llvm::StringRef File) { + [Mangler = std::move(Mangler), + ContextProvider = std::move(ContextProvider)]( + const std::vector<std::string> &Args, llvm::StringRef File) { + llvm::SmallString<256> AbsFile(File); + llvm::sys::fs::make_absolute(AbsFile); + clang::clangd::WithContext WithCfg(ContextProvider(AbsFile)); + clang::tooling::CompileCommand Cmd; Cmd.CommandLine = Args; Mangler->operator()(Cmd, File); diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config.test b/clang-tools-extra/clangd/test/indexer-clangd-config.test new file mode 100644 index 0000000000000..f99f2db410ee3 --- /dev/null +++ b/clang-tools-extra/clangd/test/indexer-clangd-config.test @@ -0,0 +1,25 @@ +# Test that clangd-indexer respects CompileFlags + +# RUN: rm -rf %t.dir && mkdir -p %t.dir +# RUN: split-file %s %t.dir + +# By default config is enabled, so the .clangd file's CompileFlags.Add should +# define FROM_CLANGD_FILE, and the #error below must not fire. +# RUN: clangd-indexer %t.dir/test.cpp 2>&1 | FileCheck --check-prefix=ENABLED %s +# ENABLED-NOT: error: "FROM_CLANGD_FILE not defined + +# With config disabled, the macro isn't defined by the .clangd file, so the +# #error must fire. +# RUN: clangd-indexer --enable-config=0 %t.dir/test.cpp 2>&1 \ +# RUN: | FileCheck --check-prefix=DISABLED %s +# DISABLED: error: "FROM_CLANGD_FILE not defined - .clangd file was not applied" + +#--- .clangd +CompileFlags: + Add: [-DFROM_CLANGD_FILE=1] + +#--- test.cpp +#ifndef FROM_CLANGD_FILE +#error "FROM_CLANGD_FILE not defined - .clangd file was not applied" +#endif +int foo() { return 0; } diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 13fe4d3911731..f95e2dbf5eb38 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -489,14 +489,7 @@ opt<bool> PrettyPrint{ opt<bool> EnableConfig{ "enable-config", cat(Misc), - desc( - "Read user and project configuration from YAML files.\n" - "Project config is from a .clangd file in the project directory.\n" - "User config is from clangd/config.yaml in the following directories:\n" - "\tWindows: %USERPROFILE%\\AppData\\Local\n" - "\tMac OS: ~/Library/Preferences/\n" - "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n" - "Configuration is documented at https://clangd.llvm.org/config.html"), + desc(config::Provider::EnableConfigFlagDesc), init(true), }; @@ -998,19 +991,8 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var RealThreadsafeFS TFS; std::vector<std::unique_ptr<config::Provider>> ProviderStack; std::unique_ptr<config::Provider> Config; - if (EnableConfig) { - ProviderStack.push_back( - config::Provider::fromAncestorRelativeYAMLFiles(".clangd", TFS)); - llvm::SmallString<256> UserConfig; - if (llvm::sys::path::user_config_directory(UserConfig)) { - llvm::sys::path::append(UserConfig, "clangd", "config.yaml"); - vlog("User config file is {0}", UserConfig); - ProviderStack.push_back(config::Provider::fromYAMLFile( - UserConfig, /*Directory=*/"", TFS, /*Trusted=*/true)); - } else { - elog("Couldn't determine user config file, not loading"); - } - } + if (EnableConfig) + ProviderStack = config::Provider::createDefaultProviders(TFS); ProviderStack.push_back(std::make_unique<FlagsConfigProvider>()); std::vector<const config::Provider *> ProviderPointers; for (const auto &P : ProviderStack) >From d19475d436d308f26bc71d9fe2f0caffb14674c4 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Tue, 11 Aug 2026 20:58:31 +0300 Subject: [PATCH 02/18] Disable config by default --- .../clangd/indexer/IndexerMain.cpp | 2 +- .../clangd/test/indexer-clangd-config.test | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index c330be298a87a..154374606e38d 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -59,7 +59,7 @@ static llvm::cl::list<std::string> QueryDriverGlobs{ static llvm::cl::opt<bool> EnableConfig{ "enable-config", llvm::cl::desc(config::Provider::EnableConfigFlagDesc), - llvm::cl::init(true), + llvm::cl::init(false), }; class IndexActionFactory : public tooling::FrontendActionFactory { diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config.test b/clang-tools-extra/clangd/test/indexer-clangd-config.test index f99f2db410ee3..c87e6fb1f1e28 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config.test @@ -3,17 +3,17 @@ # RUN: rm -rf %t.dir && mkdir -p %t.dir # RUN: split-file %s %t.dir -# By default config is enabled, so the .clangd file's CompileFlags.Add should -# define FROM_CLANGD_FILE, and the #error below must not fire. -# RUN: clangd-indexer %t.dir/test.cpp 2>&1 | FileCheck --check-prefix=ENABLED %s -# ENABLED-NOT: error: "FROM_CLANGD_FILE not defined - -# With config disabled, the macro isn't defined by the .clangd file, so the -# #error must fire. -# RUN: clangd-indexer --enable-config=0 %t.dir/test.cpp 2>&1 \ -# RUN: | FileCheck --check-prefix=DISABLED %s +# By default config is disabled, so the .clangd file's CompileFlags.Add is not +# applied and the #error below must fire. +# RUN: clangd-indexer %t.dir/test.cpp 2>&1 | FileCheck --check-prefix=DISABLED %s # DISABLED: error: "FROM_CLANGD_FILE not defined - .clangd file was not applied" +# With config enabled, the .clangd file's CompileFlags.Add should define +# FROM_CLANGD_FILE, so the #error below must not fire. +# RUN: clangd-indexer --enable-config %t.dir/test.cpp 2>&1 \ +# RUN: | FileCheck --check-prefix=ENABLED %s +# ENABLED-NOT: error: "FROM_CLANGD_FILE not defined + #--- .clangd CompileFlags: Add: [-DFROM_CLANGD_FILE=1] >From 4b135375aaa35082fa0769a4fda6ea46fd3f400e Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Tue, 11 Aug 2026 23:22:05 +0300 Subject: [PATCH 03/18] move some logic into combineOwned --- clang-tools-extra/clangd/ConfigProvider.cpp | 9 +++++++++ clang-tools-extra/clangd/ConfigProvider.h | 15 +++++++++++++++ clang-tools-extra/clangd/indexer/IndexerMain.cpp | 7 ++----- clang-tools-extra/clangd/tool/ClangdMain.cpp | 8 ++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/clang-tools-extra/clangd/ConfigProvider.cpp b/clang-tools-extra/clangd/ConfigProvider.cpp index e26509d8b1c8b..a5bfee9ec303f 100644 --- a/clang-tools-extra/clangd/ConfigProvider.cpp +++ b/clang-tools-extra/clangd/ConfigProvider.cpp @@ -168,6 +168,15 @@ Provider::combine(std::vector<const Provider *> Providers) { return std::make_unique<CombinedProvider>(std::move(Providers)); } +Provider::OwningProvider +Provider::combineOwned(std::vector<std::unique_ptr<Provider>> Sources) { + std::vector<const Provider *> Pointers; + Pointers.reserve(Sources.size()); + for (const auto &P : Sources) + Pointers.push_back(P.get()); + return {combine(std::move(Pointers)), std::move(Sources)}; +} + std::vector<std::unique_ptr<Provider>> Provider::createDefaultProviders(const ThreadsafeFS &TFS) { std::vector<std::unique_ptr<Provider>> Providers; diff --git a/clang-tools-extra/clangd/ConfigProvider.h b/clang-tools-extra/clangd/ConfigProvider.h index e1a52604e7de8..9f5eeee2c8456 100644 --- a/clang-tools-extra/clangd/ConfigProvider.h +++ b/clang-tools-extra/clangd/ConfigProvider.h @@ -90,6 +90,21 @@ class Provider { static std::vector<std::unique_ptr<Provider>> createDefaultProviders(const ThreadsafeFS &); + /// The result of combining several providers, bundled together with the + /// providers themselves. combine() only stores raw pointers to the + /// providers it combines, so those providers must outlive it; keeping + /// them together in one movable object (rather than as separate + /// same-scope variables at the call site) makes it hard to accidentally + /// let them go out of scope before Combined does. + struct OwningProvider { + std::unique_ptr<Provider> Combined; + std::vector<std::unique_ptr<Provider>> Sources; + }; + + /// Like combine(), but takes ownership of the providers being combined. + static OwningProvider + combineOwned(std::vector<std::unique_ptr<Provider>> Sources); + /// Build a config based on this provider. Config getConfig(const Params &, DiagnosticCallback) const; diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 154374606e38d..9479106e829ea 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -171,14 +171,11 @@ int main(int argc, const char **argv) { if (clang::clangd::EnableConfig) ProviderStack = clang::clangd::config::Provider::createDefaultProviders(TFS); - std::vector<const clang::clangd::config::Provider *> ProviderPointers; - for (const auto &P : ProviderStack) - ProviderPointers.push_back(P.get()); auto ConfigProvider = - clang::clangd::config::Provider::combine(std::move(ProviderPointers)); + clang::clangd::config::Provider::combineOwned(std::move(ProviderStack)); auto ContextProvider = clang::clangd::ClangdServer::createConfiguredContextProvider( - ConfigProvider.get(), /*Callbacks=*/nullptr); + ConfigProvider.Combined.get(), /*Callbacks=*/nullptr); // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index f95e2dbf5eb38..c0bf11a6c8a44 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -990,15 +990,11 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var RealThreadsafeFS TFS; std::vector<std::unique_ptr<config::Provider>> ProviderStack; - std::unique_ptr<config::Provider> Config; if (EnableConfig) ProviderStack = config::Provider::createDefaultProviders(TFS); ProviderStack.push_back(std::make_unique<FlagsConfigProvider>()); - std::vector<const config::Provider *> ProviderPointers; - for (const auto &P : ProviderStack) - ProviderPointers.push_back(P.get()); - Config = config::Provider::combine(std::move(ProviderPointers)); - Opts.ConfigProvider = Config.get(); + auto Config = config::Provider::combineOwned(std::move(ProviderStack)); + Opts.ConfigProvider = Config.Combined.get(); // Create an empty clang-tidy option. TidyProvider ClangTidyOptProvider; >From 4da0a7b2f22f70d343bee374323270fd0100f560 Mon Sep 17 00:00:00 2001 From: Nathan Ridge <[email protected]> Date: Mon, 31 Aug 2026 03:38:24 -0400 Subject: [PATCH 04/18] Test demonstrating a bug in the patch --- .../indexer-clangd-config-relative-path.test | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test new file mode 100644 index 0000000000000..261bcd2a3fd38 --- /dev/null +++ b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test @@ -0,0 +1,47 @@ +# Test that clangd-indexer finds the .clangd file when the compilation database +# names its source files relative to the compile command's "directory". +# +# We specify a custom path in XDG_CONFIG_HOME, which only works on some +# systems, so that the developer's own user config can't affect the result. +# UNSUPPORTED: system-windows +# UNSUPPORTED: system-darwin + +# RUN: rm -rf %t.dir && mkdir -p %t.dir +# RUN: split-file %s %t.dir +# RUN: mkdir -p %t.dir/elsewhere %t.dir/xdg + +# Two databases for the same project: one naming test.cpp relative to +# "directory", one naming it absolutely. They should behave the same. +# RUN: mkdir -p %t.dir/rel %t.dir/abs +# RUN: echo '[{"directory":"%/t.dir/proj","command":"clang++ -c test.cpp","file":"test.cpp"}]' > %t.dir/rel/compile_commands.json +# RUN: echo '[{"directory":"%/t.dir/proj","command":"clang++ -c test.cpp","file":"%/t.dir/proj/test.cpp"}]' > %t.dir/abs/compile_commands.json + +# Run from a directory that is neither the project nor the build directory, so +# that a process-CWD-relative lookup is guaranteed to look in the wrong place. + +# Relative "file" entry: the .clangd file's CompileFlags.Add should still be +# applied, so the #error must not fire. +# RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ +# RUN: clangd-indexer --enable-config --executor=all-TUs \ +# RUN: %t.dir/rel/compile_commands.json > %t.dir/rel.idx 2> %t.dir/rel.log +# RUN: FileCheck --check-prefix=REL --input-file=%t.dir/rel.log %s +# REL: Processing file {{.*}}test.cpp +# REL-NOT: error: "FROM_CLANGD_FILE not defined + +# Absolute "file" entry: same project, same config, and this one works today. +# RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ +# RUN: clangd-indexer --enable-config --executor=all-TUs \ +# RUN: %t.dir/abs/compile_commands.json > %t.dir/abs.idx 2> %t.dir/abs.log +# RUN: FileCheck --check-prefix=ABS --input-file=%t.dir/abs.log %s +# ABS: Processing file {{.*}}test.cpp +# ABS-NOT: error: "FROM_CLANGD_FILE not defined + +#--- proj/.clangd +CompileFlags: + Add: [-DFROM_CLANGD_FILE=1] + +#--- proj/test.cpp +#ifndef FROM_CLANGD_FILE +#error "FROM_CLANGD_FILE not defined - .clangd file was not applied" +#endif +int foo() { return 0; } >From b1ce4607f8d11bccbe28f7845a4ddfdb538accb7 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Sat, 5 Sep 2026 11:24:26 +0300 Subject: [PATCH 05/18] config provider now takes ownership --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 11 ++++++----- clang-tools-extra/clangd/ClangdLSPServer.h | 4 ++-- clang-tools-extra/clangd/ConfigProvider.cpp | 15 +++------------ clang-tools-extra/clangd/ConfigProvider.h | 18 ++---------------- .../clangd/fuzzer/clangd-fuzzer.cpp | 2 +- .../clangd/indexer/IndexerMain.cpp | 4 ++-- clang-tools-extra/clangd/tool/Check.cpp | 12 +++++++----- clang-tools-extra/clangd/tool/ClangdMain.cpp | 9 ++++----- .../clangd/unittests/ClangdLSPServerTests.cpp | 5 ++--- .../clangd/unittests/ConfigProviderTests.cpp | 7 ++++--- 10 files changed, 33 insertions(+), 54 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 43e8b35e45c89..296b303bb5123 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1660,7 +1660,7 @@ void ClangdLSPServer::onAST(const ASTParams &Params, } ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts) + ClangdLSPServer::Options &&Opts) : ShouldProfile(/*Period=*/std::chrono::minutes(5), /*Delay=*/std::chrono::minutes(1)), ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), @@ -1668,12 +1668,13 @@ ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, BackgroundContext(Context::current().clone()), Transp(Transp), MsgHandler(new MessageHandler(*this)), TFS(TFS), SupportedSymbolKinds(defaultSymbolKinds()), - SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { - if (Opts.ConfigProvider) { - assert(!Opts.ContextProvider && + SupportedCompletionItemKinds(defaultCompletionItemKinds()), + Opts(std::move(Opts)) { + if (this->Opts.ConfigProvider) { + assert(!this->Opts.ContextProvider && "Only one of ConfigProvider and ContextProvider allowed!"); this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( - Opts.ConfigProvider, this); + this->Opts.ConfigProvider.get(), this); } LSPBinder Bind(this->Handlers, *this); Bind.method("initialize", this, &ClangdLSPServer::onInitialize); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 6ada3fd9e6e47..395c201b052ed 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -41,7 +41,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks, public: struct Options : ClangdServer::Options { /// Supplies configuration (overrides ClangdServer::ContextProvider). - config::Provider *ConfigProvider = nullptr; + std::unique_ptr<config::Provider> ConfigProvider; /// Look for compilation databases, rather than using compile commands /// set via LSP (extensions) only. bool UseDirBasedCDB = true; @@ -69,7 +69,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks, }; ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts); + ClangdLSPServer::Options &&Opts); /// The destructor blocks on any outstanding background tasks. ~ClangdLSPServer(); diff --git a/clang-tools-extra/clangd/ConfigProvider.cpp b/clang-tools-extra/clangd/ConfigProvider.cpp index a5bfee9ec303f..b8308aafafab2 100644 --- a/clang-tools-extra/clangd/ConfigProvider.cpp +++ b/clang-tools-extra/clangd/ConfigProvider.cpp @@ -146,9 +146,9 @@ Provider::fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath, } std::unique_ptr<Provider> -Provider::combine(std::vector<const Provider *> Providers) { +Provider::combine(std::vector<std::unique_ptr<Provider>> Providers) { class CombinedProvider : public Provider { - std::vector<const Provider *> Providers; + std::vector<std::unique_ptr<Provider>> Providers; std::vector<CompiledFragment> getFragments(const Params &P, DiagnosticCallback DC) const override { @@ -161,22 +161,13 @@ Provider::combine(std::vector<const Provider *> Providers) { } public: - CombinedProvider(std::vector<const Provider *> Providers) + CombinedProvider(std::vector<std::unique_ptr<Provider>> Providers) : Providers(std::move(Providers)) {} }; return std::make_unique<CombinedProvider>(std::move(Providers)); } -Provider::OwningProvider -Provider::combineOwned(std::vector<std::unique_ptr<Provider>> Sources) { - std::vector<const Provider *> Pointers; - Pointers.reserve(Sources.size()); - for (const auto &P : Sources) - Pointers.push_back(P.get()); - return {combine(std::move(Pointers)), std::move(Sources)}; -} - std::vector<std::unique_ptr<Provider>> Provider::createDefaultProviders(const ThreadsafeFS &TFS) { std::vector<std::unique_ptr<Provider>> Providers; diff --git a/clang-tools-extra/clangd/ConfigProvider.h b/clang-tools-extra/clangd/ConfigProvider.h index 9f5eeee2c8456..ccd2ed6ecc2cd 100644 --- a/clang-tools-extra/clangd/ConfigProvider.h +++ b/clang-tools-extra/clangd/ConfigProvider.h @@ -82,7 +82,8 @@ class Provider { /// A provider that includes fragments from all the supplied providers. /// Order is preserved; later providers take precedence over earlier ones. - static std::unique_ptr<Provider> combine(std::vector<const Provider *>); + static std::unique_ptr<Provider> + combine(std::vector<std::unique_ptr<Provider>>); /// Returns providers for the configuration files that clangd tools read by /// default: project config (ancestor `.clangd` files) and the user's @@ -90,21 +91,6 @@ class Provider { static std::vector<std::unique_ptr<Provider>> createDefaultProviders(const ThreadsafeFS &); - /// The result of combining several providers, bundled together with the - /// providers themselves. combine() only stores raw pointers to the - /// providers it combines, so those providers must outlive it; keeping - /// them together in one movable object (rather than as separate - /// same-scope variables at the call site) makes it hard to accidentally - /// let them go out of scope before Combined does. - struct OwningProvider { - std::unique_ptr<Provider> Combined; - std::vector<std::unique_ptr<Provider>> Sources; - }; - - /// Like combine(), but takes ownership of the providers being combined. - static OwningProvider - combineOwned(std::vector<std::unique_ptr<Provider>> Sources); - /// Build a config based on this provider. Config getConfig(const Params &, DiagnosticCallback) const; diff --git a/clang-tools-extra/clangd/fuzzer/clangd-fuzzer.cpp b/clang-tools-extra/clangd/fuzzer/clangd-fuzzer.cpp index 7295f03852e7d..1ce2f5b6370f8 100644 --- a/clang-tools-extra/clangd/fuzzer/clangd-fuzzer.cpp +++ b/clang-tools-extra/clangd/fuzzer/clangd-fuzzer.cpp @@ -35,7 +35,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *Data, size_t Size) { Opts.UseDirBasedCDB = false; // Initialize and run ClangdLSPServer. - ClangdLSPServer LSPServer(*Transport, FS, Opts); + ClangdLSPServer LSPServer(*Transport, FS, std::move(Opts)); LSPServer.run(); return 0; } diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 9479106e829ea..bbf7200d3b657 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -172,10 +172,10 @@ int main(int argc, const char **argv) { ProviderStack = clang::clangd::config::Provider::createDefaultProviders(TFS); auto ConfigProvider = - clang::clangd::config::Provider::combineOwned(std::move(ProviderStack)); + clang::clangd::config::Provider::combine(std::move(ProviderStack)); auto ContextProvider = clang::clangd::ClangdServer::createConfiguredContextProvider( - ConfigProvider.Combined.get(), /*Callbacks=*/nullptr); + ConfigProvider.get(), /*Callbacks=*/nullptr); // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp index 03c4f58a49c9c..8284454c6cb5a 100644 --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -142,7 +142,7 @@ std::vector<std::string> listTidyChecks(llvm::StringRef Glob) { class Checker { // from constructor std::string File; - ClangdLSPServer::Options Opts; + const ClangdLSPServer::Options &Opts; // from buildCommand tooling::CompileCommand Cmd; std::unique_ptr<GlobalCompilationDatabase> BaseCDB; @@ -463,7 +463,7 @@ class Checker { } // namespace bool check(llvm::StringRef File, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts) { + ClangdLSPServer::Options &&Opts) { std::optional<Range> LineRange; if (!CheckFileLines.empty()) { uint32_t Begin = 0, End = std::numeric_limits<uint32_t>::max(); @@ -510,9 +510,11 @@ bool check(llvm::StringRef File, const ThreadsafeFS &TFS, F.Diagnostics.ClangTidy.FastCheckFilter.emplace("None"); return {std::move(F).compile(Diag)}; } - } OverrideConfig; - auto ConfigProvider = - config::Provider::combine({Opts.ConfigProvider, &OverrideConfig}); + }; + std::vector<std::unique_ptr<config::Provider>> ConfigProviders; + ConfigProviders.push_back(std::move(Opts.ConfigProvider)); + ConfigProviders.push_back(std::make_unique<OverrideConfigProvider>()); + auto ConfigProvider = config::Provider::combine(std::move(ConfigProviders)); auto ContextProvider = ClangdServer::createConfiguredContextProvider( ConfigProvider.get(), nullptr); diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index c0bf11a6c8a44..cb8bcef990cac 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -66,7 +66,7 @@ namespace clangd { // Implemented in Check.cpp. bool check(const llvm::StringRef File, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts); + ClangdLSPServer::Options &&Opts); namespace { @@ -993,8 +993,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var if (EnableConfig) ProviderStack = config::Provider::createDefaultProviders(TFS); ProviderStack.push_back(std::make_unique<FlagsConfigProvider>()); - auto Config = config::Provider::combineOwned(std::move(ProviderStack)); - Opts.ConfigProvider = Config.Combined.get(); + Opts.ConfigProvider = config::Provider::combine(std::move(ProviderStack)); // Create an empty clang-tidy option. TidyProvider ClangTidyOptProvider; @@ -1033,7 +1032,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var return 1; } log("Entering check mode (no LSP server)"); - return check(Path, TFS, Opts) + return check(Path, TFS, std::move(Opts)) ? 0 : static_cast<int>(ErrorResultCode::CheckFailed); } @@ -1070,7 +1069,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var std::move(*Mappings)); } - ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts); + ClangdLSPServer LSPServer(*TransportLayer, TFS, std::move(Opts)); llvm::set_thread_name("clangd.main"); int ExitCode = LSPServer.run() ? 0 diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp index 5d6a69c953e17..551504ffb6544 100644 --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -67,7 +67,7 @@ class LSPTest : public ::testing::Test { LSPClient &start() { EXPECT_FALSE(Server) << "Already initialized"; - Server.emplace(Client.transport(), FS, Opts); + Server.emplace(Client.transport(), FS, std::move(Opts)); ServerThread.emplace([&] { EXPECT_TRUE(Server->run()); }); Client.call("initialize", llvm::json::Object{}); return Client; @@ -308,9 +308,8 @@ TEST_F(LSPTest, IncomingCalls) { } TEST_F(LSPTest, CDBConfigIntegration) { - auto CfgProvider = + Opts.ConfigProvider = config::Provider::fromAncestorRelativeYAMLFiles(".clangd", FS); - Opts.ConfigProvider = CfgProvider.get(); // Map bar.cpp to a different compilation database which defines FOO->BAR. FS.Files[".clangd"] = R"yaml( diff --git a/clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp b/clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp index 36a9c383e058e..d69d708a464d5 100644 --- a/clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp @@ -58,9 +58,10 @@ std::vector<std::string> getAddedArgs(Config &C) { // cache their results. TEST(ProviderTest, Combine) { CapturedDiags Diags; - FakeProvider Foo("foo"); - FakeProvider Bar("bar"); - auto Combined = Provider::combine({&Foo, &Bar}); + std::vector<std::unique_ptr<Provider>> Providers; + Providers.push_back(std::make_unique<FakeProvider>("foo")); + Providers.push_back(std::make_unique<FakeProvider>("bar")); + auto Combined = Provider::combine(std::move(Providers)); Config Cfg = Combined->getConfig(Params(), Diags.callback()); EXPECT_THAT(Diags.Diagnostics, ElementsAre(diagMessage("foo"), diagMessage("bar"))); >From a02e0e6c2295a001465b800560f5194a14392fa2 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Sat, 5 Sep 2026 11:46:59 +0300 Subject: [PATCH 06/18] Change test on actual behaviour --- .../clangd/test/indexer-clangd-config-relative-path.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test index 261bcd2a3fd38..e2b08fc25b323 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test @@ -20,13 +20,13 @@ # that a process-CWD-relative lookup is guaranteed to look in the wrong place. # Relative "file" entry: the .clangd file's CompileFlags.Add should still be -# applied, so the #error must not fire. +# applied, so the #error must not fire, but indexer can't handle it yet. # RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ # RUN: clangd-indexer --enable-config --executor=all-TUs \ # RUN: %t.dir/rel/compile_commands.json > %t.dir/rel.idx 2> %t.dir/rel.log # RUN: FileCheck --check-prefix=REL --input-file=%t.dir/rel.log %s # REL: Processing file {{.*}}test.cpp -# REL-NOT: error: "FROM_CLANGD_FILE not defined +# REL: error: "FROM_CLANGD_FILE not defined # Absolute "file" entry: same project, same config, and this one works today. # RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ >From 0b4eb731afb9a319296f3df99f4d3629e85c985a Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Mon, 7 Sep 2026 23:07:43 +0300 Subject: [PATCH 07/18] add info about parsing config files --- .../clangd/indexer/IndexerMain.cpp | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index bbf7200d3b657..47444af3450c1 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -#include "ClangdServer.h" #include "CompileCommands.h" #include "Compiler.h" +#include "Config.h" #include "ConfigProvider.h" #include "index/IndexAction.h" #include "index/Merge.h" @@ -62,6 +62,41 @@ static llvm::cl::opt<bool> EnableConfig{ llvm::cl::init(false), }; +std::function<Context(llvm::StringRef)> +createConfiguredContextProvider(const config::Provider *Provider) { + if (!Provider) + return [](llvm::StringRef) { return Context::current().clone(); }; + + return [Provider](llvm::StringRef File) { + config::Params Params; + llvm::SmallString<256> PosixPath; + if (!File.empty()) { + assert(llvm::sys::path::is_absolute(File)); + llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix); + Params.Path = PosixPath.str(); + } + + Config C = Provider->getConfig(Params, [](const llvm::SMDiagnostic &D) { + switch (D.getKind()) { + case llvm::SourceMgr::DK_Error: + elog("config error at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), + D.getColumnNo(), D.getMessage()); + break; + case llvm::SourceMgr::DK_Warning: + log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), + D.getLineNo(), D.getColumnNo(), D.getMessage()); + break; + case llvm::SourceMgr::DK_Note: + case llvm::SourceMgr::DK_Remark: + vlog("config note at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), + D.getColumnNo(), D.getMessage()); + break; + } + }); + return Context::current().derive(Config::Key, std::move(C)); + }; +} + class IndexActionFactory : public tooling::FrontendActionFactory { public: IndexActionFactory(IndexFileIn &Result) : Result(Result) {} @@ -174,8 +209,7 @@ int main(int argc, const char **argv) { auto ConfigProvider = clang::clangd::config::Provider::combine(std::move(ProviderStack)); auto ContextProvider = - clang::clangd::ClangdServer::createConfiguredContextProvider( - ConfigProvider.get(), /*Callbacks=*/nullptr); + clang::clangd::createConfiguredContextProvider(ConfigProvider.get()); // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; >From ec49f95e59fc15c297738665694de31a8dba6803 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Tue, 15 Sep 2026 23:39:25 +0300 Subject: [PATCH 08/18] fix test --- .../clangd/test/indexer-clangd-config.test | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config.test b/clang-tools-extra/clangd/test/indexer-clangd-config.test index c87e6fb1f1e28..07ff2b6a9511e 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config.test @@ -1,16 +1,22 @@ # Test that clangd-indexer respects CompileFlags +# We specify a custom path in XDG_CONFIG_HOME, which only works on some +# systems, so that the developer's own user config can't affect the result. +# UNSUPPORTED: system-windows +# UNSUPPORTED: system-darwin + # RUN: rm -rf %t.dir && mkdir -p %t.dir # RUN: split-file %s %t.dir # By default config is disabled, so the .clangd file's CompileFlags.Add is not # applied and the #error below must fire. -# RUN: clangd-indexer %t.dir/test.cpp 2>&1 | FileCheck --check-prefix=DISABLED %s +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer %t.dir/test.cpp 2>&1 \ +# RUN: | FileCheck --check-prefix=DISABLED %s # DISABLED: error: "FROM_CLANGD_FILE not defined - .clangd file was not applied" # With config enabled, the .clangd file's CompileFlags.Add should define # FROM_CLANGD_FILE, so the #error below must not fire. -# RUN: clangd-indexer --enable-config %t.dir/test.cpp 2>&1 \ +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer --enable-config %t.dir/test.cpp 2>&1 \ # RUN: | FileCheck --check-prefix=ENABLED %s # ENABLED-NOT: error: "FROM_CLANGD_FILE not defined >From 4e5106893fa02b30f4692f4eb5060f47c97aa30b Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Tue, 15 Sep 2026 23:42:45 +0300 Subject: [PATCH 09/18] separate stderr and stdout in test --- .../clangd/test/indexer-clangd-config.test | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config.test b/clang-tools-extra/clangd/test/indexer-clangd-config.test index 07ff2b6a9511e..440070d375821 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config.test @@ -10,14 +10,16 @@ # By default config is disabled, so the .clangd file's CompileFlags.Add is not # applied and the #error below must fire. -# RUN: env XDG_CONFIG_HOME=%t clangd-indexer %t.dir/test.cpp 2>&1 \ -# RUN: | FileCheck --check-prefix=DISABLED %s +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer %t.dir/test.cpp \ +# RUN: > %t.dir/disabled.idx 2> %t.dir/disabled.log +# RUN: FileCheck --check-prefix=DISABLED --input-file=%t.dir/disabled.log %s # DISABLED: error: "FROM_CLANGD_FILE not defined - .clangd file was not applied" # With config enabled, the .clangd file's CompileFlags.Add should define # FROM_CLANGD_FILE, so the #error below must not fire. -# RUN: env XDG_CONFIG_HOME=%t clangd-indexer --enable-config %t.dir/test.cpp 2>&1 \ -# RUN: | FileCheck --check-prefix=ENABLED %s +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer --enable-config %t.dir/test.cpp \ +# RUN: > %t.dir/enabled.idx 2> %t.dir/enabled.log +# RUN: FileCheck --check-prefix=ENABLED --input-file=%t.dir/enabled.log %s # ENABLED-NOT: error: "FROM_CLANGD_FILE not defined #--- .clangd >From b7aefa3d595a1f21f472bfcbdf54de9bd28547e2 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Tue, 15 Sep 2026 23:50:29 +0300 Subject: [PATCH 10/18] add positive check to test --- .../clangd/test/indexer-clangd-config.test | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config.test b/clang-tools-extra/clangd/test/indexer-clangd-config.test index 440070d375821..ce67f9d1eb183 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config.test @@ -7,26 +7,28 @@ # RUN: rm -rf %t.dir && mkdir -p %t.dir # RUN: split-file %s %t.dir +# RUN: echo '[{"directory":"%/t.dir/proj","command":"clang++ -c test.cpp","file":"%/t.dir/proj/test.cpp"}]' > %t.dir/compile_commands.json # By default config is disabled, so the .clangd file's CompileFlags.Add is not # applied and the #error below must fire. -# RUN: env XDG_CONFIG_HOME=%t clangd-indexer %t.dir/test.cpp \ +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer %t.dir/proj/test.cpp \ # RUN: > %t.dir/disabled.idx 2> %t.dir/disabled.log # RUN: FileCheck --check-prefix=DISABLED --input-file=%t.dir/disabled.log %s # DISABLED: error: "FROM_CLANGD_FILE not defined - .clangd file was not applied" # With config enabled, the .clangd file's CompileFlags.Add should define # FROM_CLANGD_FILE, so the #error below must not fire. -# RUN: env XDG_CONFIG_HOME=%t clangd-indexer --enable-config %t.dir/test.cpp \ -# RUN: > %t.dir/enabled.idx 2> %t.dir/enabled.log +# RUN: env XDG_CONFIG_HOME=%t clangd-indexer --enable-config --executor=all-TUs \ +# RUN: %t.dir/compile_commands.json > %t.dir/enabled.idx 2> %t.dir/enabled.log # RUN: FileCheck --check-prefix=ENABLED --input-file=%t.dir/enabled.log %s +# ENABLED: Processing file {{.*}}test.cpp # ENABLED-NOT: error: "FROM_CLANGD_FILE not defined -#--- .clangd +#--- proj/.clangd CompileFlags: Add: [-DFROM_CLANGD_FILE=1] -#--- test.cpp +#--- proj/test.cpp #ifndef FROM_CLANGD_FILE #error "FROM_CLANGD_FILE not defined - .clangd file was not applied" #endif >From 3ec35e7d4e3e945996182682c5e3f20ac1e2802e Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Wed, 16 Sep 2026 23:37:50 +0300 Subject: [PATCH 11/18] add comments about current limitations --- clang-tools-extra/clangd/indexer/IndexerMain.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 47444af3450c1..4fd86f871c482 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -224,8 +224,17 @@ int main(int argc, const char **argv) { [Mangler = std::move(Mangler), ContextProvider = std::move(ContextProvider)]( const std::vector<std::string> &Args, llvm::StringRef File) { + // Issue: If File is relative, it's relative to the compile command's + // "directory", not our CWD, but ToolExecutor doesn't expose + // "directory" here, so make_absolute can resolve it wrong and + // miss the .clangd file. See indexer-clangd-config-relative-path.test. llvm::SmallString<256> AbsFile(File); llvm::sys::fs::make_absolute(AbsFile); + // Issue: WithCfg only lives for this ArgumentsAdjuster call, so it's + // visible to Mangler below but not to the parse that follows. + // That's harmless today since clangd-indexer doesn't consult + // config during the parse, but a real fix would need libTooling + // changes to keep the context alive for the whole invocation. clang::clangd::WithContext WithCfg(ContextProvider(AbsFile)); clang::tooling::CompileCommand Cmd; >From 812a068213fe523685a1553777479c79b67b7f70 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Thu, 24 Sep 2026 13:39:03 +0300 Subject: [PATCH 12/18] format code --- clang-tools-extra/clangd/indexer/IndexerMain.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 4fd86f871c482..2bc4bfaf860ba 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -224,14 +224,15 @@ int main(int argc, const char **argv) { [Mangler = std::move(Mangler), ContextProvider = std::move(ContextProvider)]( const std::vector<std::string> &Args, llvm::StringRef File) { - // Issue: If File is relative, it's relative to the compile command's - // "directory", not our CWD, but ToolExecutor doesn't expose - // "directory" here, so make_absolute can resolve it wrong and - // miss the .clangd file. See indexer-clangd-config-relative-path.test. + // Issue: If File is relative, it's relative to the compile + // command's "directory", not our CWD, but ToolExecutor doesn't + // expose "directory" here, so make_absolute can resolve it wrong + // and miss the .clangd file. See + // indexer-clangd-config-relative-path.test. llvm::SmallString<256> AbsFile(File); llvm::sys::fs::make_absolute(AbsFile); - // Issue: WithCfg only lives for this ArgumentsAdjuster call, so it's - // visible to Mangler below but not to the parse that follows. + // Issue: WithCfg only lives for this ArgumentsAdjuster call, so + // it's visible to Mangler below but not to the parse that follows. // That's harmless today since clangd-indexer doesn't consult // config during the parse, but a real fix would need libTooling // changes to keep the context alive for the whole invocation. >From 62ce4202c4f38706bace01ac12d959b2e94aa53a Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:09:45 +0300 Subject: [PATCH 13/18] format code --- clang-tools-extra/clangd/ConfigProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/ConfigProvider.h b/clang-tools-extra/clangd/ConfigProvider.h index ccd2ed6ecc2cd..2a965cd8ffe2d 100644 --- a/clang-tools-extra/clangd/ConfigProvider.h +++ b/clang-tools-extra/clangd/ConfigProvider.h @@ -83,7 +83,7 @@ class Provider { /// A provider that includes fragments from all the supplied providers. /// Order is preserved; later providers take precedence over earlier ones. static std::unique_ptr<Provider> - combine(std::vector<std::unique_ptr<Provider>>); + combine(std::vector<std::unique_ptr<Provider>>); /// Returns providers for the configuration files that clangd tools read by /// default: project config (ancestor `.clangd` files) and the user's >From 054bb7577a585c78d1ee6a693d9db554f8a6ebd4 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:16:34 +0300 Subject: [PATCH 14/18] Revert "add info about parsing config files" This reverts commit 0b4eb731afb9a319296f3df99f4d3629e85c985a. --- .../clangd/indexer/IndexerMain.cpp | 40 ++----------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 2bc4bfaf860ba..0af8624080c31 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// +#include "ClangdServer.h" #include "CompileCommands.h" #include "Compiler.h" -#include "Config.h" #include "ConfigProvider.h" #include "index/IndexAction.h" #include "index/Merge.h" @@ -62,41 +62,6 @@ static llvm::cl::opt<bool> EnableConfig{ llvm::cl::init(false), }; -std::function<Context(llvm::StringRef)> -createConfiguredContextProvider(const config::Provider *Provider) { - if (!Provider) - return [](llvm::StringRef) { return Context::current().clone(); }; - - return [Provider](llvm::StringRef File) { - config::Params Params; - llvm::SmallString<256> PosixPath; - if (!File.empty()) { - assert(llvm::sys::path::is_absolute(File)); - llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix); - Params.Path = PosixPath.str(); - } - - Config C = Provider->getConfig(Params, [](const llvm::SMDiagnostic &D) { - switch (D.getKind()) { - case llvm::SourceMgr::DK_Error: - elog("config error at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), - D.getColumnNo(), D.getMessage()); - break; - case llvm::SourceMgr::DK_Warning: - log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), - D.getLineNo(), D.getColumnNo(), D.getMessage()); - break; - case llvm::SourceMgr::DK_Note: - case llvm::SourceMgr::DK_Remark: - vlog("config note at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), - D.getColumnNo(), D.getMessage()); - break; - } - }); - return Context::current().derive(Config::Key, std::move(C)); - }; -} - class IndexActionFactory : public tooling::FrontendActionFactory { public: IndexActionFactory(IndexFileIn &Result) : Result(Result) {} @@ -209,7 +174,8 @@ int main(int argc, const char **argv) { auto ConfigProvider = clang::clangd::config::Provider::combine(std::move(ProviderStack)); auto ContextProvider = - clang::clangd::createConfiguredContextProvider(ConfigProvider.get()); + clang::clangd::ClangdServer::createConfiguredContextProvider( + ConfigProvider.get(), /*Callbacks=*/nullptr); // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; >From 698bf2305fef6290b2b19e63625fba166ac8099f Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:17:43 +0300 Subject: [PATCH 15/18] replace issue by FIXME in comments --- clang-tools-extra/clangd/indexer/IndexerMain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index 0af8624080c31..a18ce9ef81921 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -190,14 +190,14 @@ int main(int argc, const char **argv) { [Mangler = std::move(Mangler), ContextProvider = std::move(ContextProvider)]( const std::vector<std::string> &Args, llvm::StringRef File) { - // Issue: If File is relative, it's relative to the compile + // FIXME: If File is relative, it's relative to the compile // command's "directory", not our CWD, but ToolExecutor doesn't // expose "directory" here, so make_absolute can resolve it wrong // and miss the .clangd file. See // indexer-clangd-config-relative-path.test. llvm::SmallString<256> AbsFile(File); llvm::sys::fs::make_absolute(AbsFile); - // Issue: WithCfg only lives for this ArgumentsAdjuster call, so + // FIXME: WithCfg only lives for this ArgumentsAdjuster call, so // it's visible to Mangler below but not to the parse that follows. // That's harmless today since clangd-indexer doesn't consult // config during the parse, but a real fix would need libTooling >From 70496230786403a59b82ee5e9c05acb32e01fad0 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:22:29 +0300 Subject: [PATCH 16/18] move TFS declaration before Opts declaration --- clang-tools-extra/clangd/tool/ClangdMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index cb8bcef990cac..d7cdcd55292ef 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -926,6 +926,7 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var log("Env {0}: {1}", EnvVar, *Val); } + RealThreadsafeFS TFS; ClangdLSPServer::Options Opts; Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs); Opts.EnableExperimentalModulesSupport = ExperimentalModulesSupport; @@ -988,7 +989,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var // external decls, since currently the index doesn't support C++20 modules. Opts.CodeComplete.ForceLoadPreamble = ExperimentalModulesSupport; - RealThreadsafeFS TFS; std::vector<std::unique_ptr<config::Provider>> ProviderStack; if (EnableConfig) ProviderStack = config::Provider::createDefaultProviders(TFS); >From f3d6c5a44cd0ee275a1a048eb448701bd09403fb Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:26:18 +0300 Subject: [PATCH 17/18] change context only for absolute paths --- clang-tools-extra/clangd/indexer/IndexerMain.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp index a18ce9ef81921..94e6b59aa9db3 100644 --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -202,7 +202,10 @@ int main(int argc, const char **argv) { // That's harmless today since clangd-indexer doesn't consult // config during the parse, but a real fix would need libTooling // changes to keep the context alive for the whole invocation. - clang::clangd::WithContext WithCfg(ContextProvider(AbsFile)); + std::optional<clang::clangd::WithContext> WithCfg; + if (llvm::sys::path::is_absolute(File)) { + WithCfg.emplace(ContextProvider(File)); + } clang::tooling::CompileCommand Cmd; Cmd.CommandLine = Args; >From 323584a433b78451943bab42c560110d1163b3c3 Mon Sep 17 00:00:00 2001 From: Fil-Den <[email protected]> Date: Fri, 25 Sep 2026 12:29:41 +0300 Subject: [PATCH 18/18] mark test as expectedly failed --- .../clangd/test/indexer-clangd-config-relative-path.test | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test index e2b08fc25b323..8f4f22c50a132 100644 --- a/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test +++ b/clang-tools-extra/clangd/test/indexer-clangd-config-relative-path.test @@ -5,6 +5,10 @@ # systems, so that the developer's own user config can't affect the result. # UNSUPPORTED: system-windows # UNSUPPORTED: system-darwin +# +# FIXME: clangd-indexer does not yet find the .clangd file for relative "file" +# entries, so the REL check below fails. Remove this XFAIL once that is fixed. +# XFAIL: * # RUN: rm -rf %t.dir && mkdir -p %t.dir # RUN: split-file %s %t.dir @@ -20,13 +24,13 @@ # that a process-CWD-relative lookup is guaranteed to look in the wrong place. # Relative "file" entry: the .clangd file's CompileFlags.Add should still be -# applied, so the #error must not fire, but indexer can't handle it yet. +# applied, so the #error must not fire. # RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ # RUN: clangd-indexer --enable-config --executor=all-TUs \ # RUN: %t.dir/rel/compile_commands.json > %t.dir/rel.idx 2> %t.dir/rel.log # RUN: FileCheck --check-prefix=REL --input-file=%t.dir/rel.log %s # REL: Processing file {{.*}}test.cpp -# REL: error: "FROM_CLANGD_FILE not defined +# REL-NOT: error: "FROM_CLANGD_FILE not defined # Absolute "file" entry: same project, same config, and this one works today. # RUN: cd %t.dir/elsewhere && env XDG_CONFIG_HOME=%t.dir/xdg \ _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
