https://github.com/ArcsinX updated https://github.com/llvm/llvm-project/pull/189284
>From 7c0a8cbff6e0fb405ca75f1688a8dab92325f63f Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <[email protected]> Date: Sun, 29 Mar 2026 23:07:37 +0300 Subject: [PATCH 1/3] [clangd] Introduce --skip-preamble-build command line options This option allows to disable preamble optimization in clangd. By default it's false, but became true if experimental modules support is enabled. --- clang-tools-extra/clangd/ClangdServer.cpp | 3 +++ clang-tools-extra/clangd/ClangdServer.h | 5 ++++ clang-tools-extra/clangd/CodeComplete.cpp | 8 ++++-- clang-tools-extra/clangd/Compiler.h | 2 ++ clang-tools-extra/clangd/ParsedAST.cpp | 2 +- clang-tools-extra/clangd/Preamble.cpp | 27 +++++++++++++++----- clang-tools-extra/clangd/Preamble.h | 4 +++ clang-tools-extra/clangd/tool/ClangdMain.cpp | 18 +++++++++++++ 8 files changed, 59 insertions(+), 10 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index f1a87dd12d905..af80cc9c3e453 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -223,6 +223,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, UseDirtyHeaders(Opts.UseDirtyHeaders), LineFoldingOnly(Opts.LineFoldingOnly), PreambleParseForwardingFunctions(Opts.PreambleParseForwardingFunctions), + SkipPreambleBuild(Opts.SkipPreambleBuild), ImportInsertions(Opts.ImportInsertions), PublishInactiveRegions(Opts.PublishInactiveRegions), WorkspaceRoot(Opts.WorkspaceRoot), @@ -300,6 +301,7 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, std::string ActualVersion = DraftMgr.addDraft(File, Version, Contents); ParseOptions Opts; Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; + Opts.SkipPreambleBuild = SkipPreambleBuild; Opts.ImportInsertions = ImportInsertions; // Compile command is set asynchronously during update, as it can be slow. @@ -459,6 +461,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, Config::current().Completion.HeaderInsertion; CodeCompleteOpts.CodePatterns = Config::current().Completion.CodePatterns; CodeCompleteOpts.MacroFilter = Config::current().Completion.MacroFilter; + ParseInput.Opts.SkipPreambleBuild = SkipPreambleBuild; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 3ffaf67553dce..6ee1dfd863e3c 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -191,6 +191,9 @@ class ClangdServer { // If true, parse emplace-like functions in the preamble. bool PreambleParseForwardingFunctions = true; + // If true, skip preamble build. + bool SkipPreambleBuild = false; + /// Whether include fixer insertions for Objective-C code should use #import /// instead of #include. bool ImportInsertions = false; @@ -508,6 +511,8 @@ class ClangdServer { bool PreambleParseForwardingFunctions = true; + bool SkipPreambleBuild = false; + bool ImportInsertions = false; bool PublishInactiveRegions = false; diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 28f81cd5267d5..8414ffe20526a 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1420,7 +1420,8 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, // overriding the preamble will break sema completion. Fortunately we can just // skip all includes in this case; these completions are really simple. PreambleBounds PreambleRegion = - ComputePreambleBounds(CI->getLangOpts(), *ContentsBuffer, 0); + computePreambleBounds(CI->getLangOpts(), *ContentsBuffer, + Input.ParseInput.Opts.SkipPreambleBuild); bool CompletingInPreamble = Input.Offset < PreambleRegion.Size || (!PreambleRegion.PreambleEndsAtStartOfLine && Input.Offset == PreambleRegion.Size); @@ -1433,7 +1434,10 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, if (Input.Preamble.StatCache) VFS = Input.Preamble.StatCache->getConsumingFS(std::move(VFS)); auto Clang = prepareCompilerInstance( - std::move(CI), !CompletingInPreamble ? &Input.Preamble.Preamble : nullptr, + std::move(CI), + (!CompletingInPreamble && !Input.ParseInput.Opts.SkipPreambleBuild) + ? &Input.Preamble.Preamble + : nullptr, std::move(ContentsBuffer), std::move(VFS), IgnoreDiags); Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble; Clang->setCodeCompletionConsumer(Consumer.release()); diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h index e513e4c40794a..5e5e23d5b9682 100644 --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -43,6 +43,8 @@ struct ParseOptions { bool PreambleParseForwardingFunctions = true; bool ImportInsertions = false; + + bool SkipPreambleBuild = false; }; /// Information required to run clang, e.g. to parse AST or do code completion. diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index 4e873f1257a17..e2a49f384a3e9 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -464,7 +464,7 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, Patch->apply(*CI); } auto Clang = prepareCompilerInstance( - std::move(CI), PreamblePCH, + std::move(CI), Inputs.Opts.SkipPreambleBuild ? nullptr : PreamblePCH, llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, Filename), VFS, *DiagConsumer); diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index f5e512793e98e..58da7edcf3b93 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -320,8 +320,9 @@ struct ScannedPreamble { /// running preprocessor over \p Contents. Returned includes do not contain /// resolved paths. \p Cmd is used to build the compiler invocation, which might /// stat/read files. -llvm::Expected<ScannedPreamble> -scanPreamble(llvm::StringRef Contents, const tooling::CompileCommand &Cmd) { +llvm::Expected<ScannedPreamble> scanPreamble(llvm::StringRef Contents, + const tooling::CompileCommand &Cmd, + bool SkipPreambleBuild) { class EmptyFS : public ThreadsafeFS { private: llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override { @@ -345,7 +346,8 @@ scanPreamble(llvm::StringRef Contents, const tooling::CompileCommand &Cmd) { // This means we're scanning (though not preprocessing) the preamble section // twice. However, it's important to precisely follow the preamble bounds used // elsewhere. - auto Bounds = ComputePreambleBounds(CI->getLangOpts(), *ContentsBuffer, 0); + auto Bounds = computePreambleBounds(CI->getLangOpts(), *ContentsBuffer, + SkipPreambleBuild); auto PreambleContents = llvm::MemoryBuffer::getMemBufferCopy( llvm::StringRef(PI.Contents).take_front(Bounds.Size)); auto Clang = prepareCompilerInstance( @@ -576,7 +578,8 @@ buildPreamble(PathRef FileName, CompilerInvocation CI, // without those. auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName); - auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *ContentsBuffer, 0); + auto Bounds = computePreambleBounds(CI.getLangOpts(), *ContentsBuffer, + Inputs.Opts.SkipPreambleBuild); trace::Span Tracer("BuildPreamble"); SPAN_ATTACH(Tracer, "File", FileName); @@ -722,7 +725,8 @@ bool isPreambleCompatible(const PreambleData &Preamble, const CompilerInvocation &CI) { auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName); - auto Bounds = ComputePreambleBounds(CI.getLangOpts(), *ContentsBuffer, 0); + auto Bounds = computePreambleBounds(CI.getLangOpts(), *ContentsBuffer, + Inputs.Opts.SkipPreambleBuild); auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory); return compileCommandsAreEqual(Inputs.CompileCommand, Preamble.CompileCommand) && @@ -785,13 +789,15 @@ PreamblePatch PreamblePatch::create(llvm::StringRef FileName, // - If scanning for Modified fails, cannot figure out newly added ones so // there's nothing to do but generate an empty patch. auto BaselineScan = - scanPreamble(Baseline.Preamble.getContents(), Modified.CompileCommand); + scanPreamble(Baseline.Preamble.getContents(), Modified.CompileCommand, + Modified.Opts.SkipPreambleBuild); if (!BaselineScan) { elog("Failed to scan baseline of {0}: {1}", FileName, BaselineScan.takeError()); return PreamblePatch::unmodified(Baseline); } - auto ModifiedScan = scanPreamble(Modified.Contents, Modified.CompileCommand); + auto ModifiedScan = scanPreamble(Modified.Contents, Modified.CompileCommand, + Modified.Opts.SkipPreambleBuild); if (!ModifiedScan) { elog("Failed to scan modified contents of {0}: {1}", FileName, ModifiedScan.takeError()); @@ -957,5 +963,12 @@ OptionalFileEntryRef PreamblePatch::getPatchEntry(llvm::StringRef MainFilePath, auto PatchFilePath = getPatchName(MainFilePath); return SM.getFileManager().getOptionalFileRef(PatchFilePath); } + +PreambleBounds computePreambleBounds(const LangOptions &LangOpts, + const llvm::MemoryBufferRef &Buffer, + bool SkipPreambleBuild) { + return SkipPreambleBuild ? PreambleBounds(0, false) + : ComputePreambleBounds(LangOpts, Buffer, 0); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h index 7f2eb233ee1aa..2051930dae05d 100644 --- a/clang-tools-extra/clangd/Preamble.h +++ b/clang-tools-extra/clangd/Preamble.h @@ -239,6 +239,10 @@ class PreamblePatch { MainFileMacros PatchedMacros; }; +PreambleBounds computePreambleBounds(const LangOptions &LangOpts, + const llvm::MemoryBufferRef &Buffer, + bool SkipPreambleBuild); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 54af3662470db..7bedf83be14ac 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -525,6 +525,14 @@ opt<bool> PreambleParseForwardingFunctions{ init(ParseOptions().PreambleParseForwardingFunctions), }; +opt<bool> SkipPreambleBuild{ + "skip-preamble-build", + cat(Misc), + desc("If ture, skip preamble build"), + Hidden, + init(ParseOptions().SkipPreambleBuild), +}; + #if defined(__GLIBC__) && CLANGD_MALLOC_TRIM opt<bool> EnableMallocTrim{ "malloc-trim", @@ -1005,6 +1013,16 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var } Opts.UseDirtyHeaders = UseDirtyHeaders; Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; + if (ExperimentalModulesSupport && + SkipPreambleBuild.getNumOccurrences() == 0) { + Opts.SkipPreambleBuild = true; + log("Experimental C++20 modules support is enabled. This leads to " + "disabling preamble build due to instability of mixed precompiled " + "headers and C++20 modules. To enable C++20 modules together with " + "preamble builds, pass --skip-preamble-build=false command line " + "option"); + } else + Opts.SkipPreambleBuild = SkipPreambleBuild; Opts.ImportInsertions = ImportInsertions; Opts.QueryDriverGlobs = std::move(QueryDriverGlobs); Opts.TweakFilter = [&](const Tweak &T) { >From 8a2394bee5b328dea4b486562fd80cda50e3eb4d Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <[email protected]> Date: Sun, 5 Apr 2026 17:11:59 +0300 Subject: [PATCH 2/3] Add test --- .../unittests/PrerequisiteModulesTest.cpp | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp index 488d7d7e6323c..7dce0ae058b28 100644 --- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp +++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp @@ -28,6 +28,8 @@ namespace clang::clangd { namespace { +MATCHER_P(named, Name, "") { return arg.Name = Name; } + class GlobalScanningCounterProjectModules : public ProjectModules { public: GlobalScanningCounterProjectModules( @@ -830,6 +832,56 @@ int use() { return m_value; } << "\nRelative path used: " << RelativeBMPath; } +TEST_F(PrerequisiteModulesTests, ModuleImportThroughInclude) { + MockDirectoryCompilationDatabase CDB(TestDir, FS); + + Annotations UseCpp(R"cpp( +#include "Header.hpp" +void use() { + TypeFrom^Module t1; + TypeFromHeader t2; +} +)cpp"); + + CDB.addFile("M.cppm", R"cpp( +export module M; +export struct TypeFromModule {}; +)cpp"); + + CDB.addFile("Header.hpp", R"cpp( +import M; +struct TypeFromHeader {}; +)cpp"); + + CDB.addFile("Use.cpp", UseCpp.code()); + + ModulesBuilder Builder(CDB); + + auto Inputs = getInputs("Use.cpp", CDB); + Inputs.ModulesManager = &Builder; + Inputs.Opts.SkipPreambleBuild = true; + + auto CI = buildCompilerInvocation(Inputs, DiagConsumer); + ASSERT_TRUE(CI); + + auto Preamble = + buildPreamble(getFullPath("Use.cpp"), *CI, Inputs, /*StoreInMemory=*/true, + /*PeambleCallback=*/nullptr); + ASSERT_TRUE(Preamble); + EXPECT_EQ(Preamble->Preamble.getBounds().Size, 0u); + + auto AST = ParsedAST::build(getFullPath("Use.cpp"), Inputs, std::move(CI), {}, + Preamble); + ASSERT_TRUE(AST); + EXPECT_TRUE(AST->getDiagnostics().empty()); + + auto Result = codeComplete(getFullPath("Use.cpp"), UseCpp.point(), + Preamble.get(), Inputs, {}); + EXPECT_THAT(Result.Completions, + testing::UnorderedElementsAre(named("TypeFromModule"), + named("TypeFromHeader"))); +} + } // namespace } // namespace clang::clangd >From 8415c6925c447335a9db7d8708be75de49eccf93 Mon Sep 17 00:00:00 2001 From: Aleksandr Platonov <[email protected]> Date: Sun, 5 Apr 2026 17:19:41 +0300 Subject: [PATCH 3/3] Fix typo --- clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp index 7dce0ae058b28..1a5d805d730f6 100644 --- a/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp +++ b/clang-tools-extra/clangd/unittests/PrerequisiteModulesTest.cpp @@ -28,7 +28,7 @@ namespace clang::clangd { namespace { -MATCHER_P(named, Name, "") { return arg.Name = Name; } +MATCHER_P(named, Name, "") { return arg.Name == Name; } class GlobalScanningCounterProjectModules : public ProjectModules { public: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
