diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -164,6 +164,9 @@ /// If true, use the dirty buffer contents when building Preambles. bool UseDirtyHeaders = false; + // If true, parse make_unique-like functions in the preamble. + bool PreambleParseForwardingFunctions = false; + explicit operator TUScheduler::Options() const; }; // Sensible default options for use in tests. @@ -416,6 +419,8 @@ bool UseDirtyHeaders = false; + bool PreambleParseForwardingFunctions = false; + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> CachedCompletionFuzzyFindRequestByFile; diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -137,6 +137,7 @@ Opts.AsyncThreadsCount = AsyncThreadsCount; Opts.RetentionPolicy = RetentionPolicy; Opts.StorePreamblesInMemory = StorePreamblesInMemory; + Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; Opts.UpdateDebounce = UpdateDebounce; Opts.ContextProvider = ContextProvider; return Opts; @@ -148,7 +149,9 @@ : FeatureModules(Opts.FeatureModules), CDB(CDB), TFS(TFS), DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr), ClangTidyProvider(Opts.ClangTidyProvider), - UseDirtyHeaders(Opts.UseDirtyHeaders), WorkspaceRoot(Opts.WorkspaceRoot), + UseDirtyHeaders(Opts.UseDirtyHeaders), + PreambleParseForwardingFunctions(Opts.PreambleParseForwardingFunctions), + WorkspaceRoot(Opts.WorkspaceRoot), Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate : TUScheduler::NoInvalidation), DirtyFS(std::make_unique(TFS, DraftMgr)) { @@ -910,7 +913,7 @@ // It's safe to pass in the TU, as dumpAST() does not // deserialize the preamble. auto Node = DynTypedNode::create( - *Inputs->AST.getASTContext().getTranslationUnitDecl()); + *Inputs->AST.getASTContext().getTranslationUnitDecl()); return CB(dumpAST(Node, Inputs->AST.getTokens(), Inputs->AST.getASTContext())); } diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h --- a/clang-tools-extra/clangd/Preamble.h +++ b/clang-tools-extra/clangd/Preamble.h @@ -100,6 +100,7 @@ std::shared_ptr buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, + bool ParseForwardingFunctions, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats = nullptr); diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -66,9 +66,10 @@ public: CppFilePreambleCallbacks( PathRef File, PreambleParsedCallback ParsedCallback, - PreambleBuildStats *Stats, + PreambleBuildStats *Stats, bool ParseForwardingFunctions, std::function BeforeExecuteCallback) : File(File), ParsedCallback(ParsedCallback), Stats(Stats), + ParseForwardingFunctions(ParseForwardingFunctions), BeforeExecuteCallback(std::move(BeforeExecuteCallback)) {} IncludeStructure takeIncludes() { return std::move(Includes); } @@ -139,12 +140,38 @@ bool shouldSkipFunctionBody(Decl *D) override { // Generally we skip function bodies in preambles for speed. // We can make exceptions for functions that are cheap to parse and - // instantiate, widely used, and valuable (e.g. commonly produce errors). - if (const auto *FT = llvm::dyn_cast(D)) { - if (const auto *II = FT->getDeclName().getAsIdentifierInfo()) - // std::make_unique is trivial, and we diagnose bad constructor calls. - if (II->isStr("make_unique") && FT->isInStdNamespace()) - return false; + // instantiate, widely used, and valuable (e.g. commonly produce errors, + // necessary for code completion/inlay hints). + if (auto *FT = llvm::dyn_cast(D)) { + // Fast path: only take care of make_unique + if (!ParseForwardingFunctions) { + if (const auto *II = FT->getDeclName().getAsIdentifierInfo()) { + // std::make_unique is trivial, and we diagnose bad constructor calls. + if (FT->isInStdNamespace() && II->isStr("make_unique")) + return false; + } + return true; + } + // Slow path: Check whether its last parameter is a parameter pack... + if (const auto *FD = FT->getAsFunction()) { + const auto NumParams = FD->getNumParams(); + if (NumParams > 0) { + const auto *LastParam = FD->getParamDecl(NumParams - 1); + if (isa(LastParam->getType())) { + const auto *PackTypePtr = + dyn_cast(LastParam->getType() + .getNonPackExpansionType() + .getNonReferenceType() + .getTypePtr()); + // ... whose template parameter comes from the function directly + if (FT->getTemplateParameters()->getDepth() == + PackTypePtr->getDepth()) { + return false; + } + } + } + return true; + } } return true; } @@ -161,6 +188,7 @@ const clang::LangOptions *LangOpts = nullptr; const SourceManager *SourceMgr = nullptr; PreambleBuildStats *Stats; + bool ParseForwardingFunctions; std::function BeforeExecuteCallback; }; @@ -426,11 +454,10 @@ } // namespace -std::shared_ptr -buildPreamble(PathRef FileName, CompilerInvocation CI, - const ParseInputs &Inputs, bool StoreInMemory, - PreambleParsedCallback PreambleCallback, - PreambleBuildStats *Stats) { +std::shared_ptr buildPreamble( + PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, + bool StoreInMemory, bool ParseForwardingFunctions, + PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats) { // Note that we don't need to copy the input contents, preamble can live // without those. auto ContentsBuffer = @@ -484,6 +511,7 @@ CI.getPreprocessorOpts().WriteCommentListToPCH = false; CppFilePreambleCallbacks CapturedInfo(FileName, PreambleCallback, Stats, + ParseForwardingFunctions, [&ASTListeners](CompilerInstance &CI) { for (const auto &L : ASTListeners) L->beforeExecute(CI); diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -204,6 +204,9 @@ /// Typically to inject per-file configuration. /// If the path is empty, context sholud be "generic". std::function ContextProvider; + + // If true, parse make_unique-like functions in the preamble. + bool PreambleParseForwardingFunctions = false; }; TUScheduler(const GlobalCompilationDatabase &CDB, const Options &Opts, diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -389,11 +389,12 @@ public: PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks, bool StorePreambleInMemory, bool RunSync, - SynchronizedTUStatus &Status, + bool ParseForwardingFunctions, SynchronizedTUStatus &Status, TUScheduler::HeaderIncluderCache &HeaderIncluders, ASTWorker &AW) : FileName(FileName), Callbacks(Callbacks), - StoreInMemory(StorePreambleInMemory), RunSync(RunSync), Status(Status), + StoreInMemory(StorePreambleInMemory), RunSync(RunSync), + ParseForwardingFunctions(ParseForwardingFunctions), Status(Status), ASTPeer(AW), HeaderIncluders(HeaderIncluders) {} /// It isn't guaranteed that each requested version will be built. If there @@ -518,6 +519,7 @@ ParsingCallbacks &Callbacks; const bool StoreInMemory; const bool RunSync; + bool ParseForwardingFunctions; SynchronizedTUStatus &Status; ASTWorker &ASTPeer; @@ -778,7 +780,8 @@ ContextProvider(Opts.ContextProvider), CDB(CDB), Callbacks(Callbacks), Barrier(Barrier), Done(false), Status(FileName, Callbacks), PreamblePeer(FileName, Callbacks, Opts.StorePreamblesInMemory, RunSync, - Status, HeaderIncluders, *this) { + Opts.PreambleParseForwardingFunctions, Status, + HeaderIncluders, *this) { // Set a fallback command because compile command can be accessed before // `Inputs` is initialized. Other fields are only used after initialization // from client inputs. @@ -1012,7 +1015,7 @@ PreambleBuildStats Stats; bool IsFirstPreamble = !LatestBuild; LatestBuild = clang::clangd::buildPreamble( - FileName, *Req.CI, Inputs, StoreInMemory, + FileName, *Req.CI, Inputs, StoreInMemory, ParseForwardingFunctions, [this, Version(Inputs.Version)](ASTContext &Ctx, Preprocessor &PP, const CanonicalIncludes &CanonIncludes) { Callbacks.onPreambleAST(FileName, Version, Ctx, PP, CanonIncludes); diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -159,6 +159,7 @@ bool buildAST() { log("Building preamble..."); Preamble = buildPreamble(File, *Invocation, Inputs, /*StoreInMemory=*/true, + Opts.PreambleParseForwardingFunctions, [&](ASTContext &Ctx, Preprocessor &PP, const CanonicalIncludes &Includes) { if (!Opts.BuildDynamicSymbolIndex) diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -498,6 +498,11 @@ Hidden, init(ClangdServer::Options().UseDirtyHeaders)}; +opt PreambleParseForwardingFunctions{ + "preamble-parse-forwarding", cat(Misc), + desc("Parse all make_unique-like functions in the preamble"), Hidden, + init(ClangdServer::Options().PreambleParseForwardingFunctions)}; + #if defined(__GLIBC__) && CLANGD_MALLOC_TRIM opt EnableMallocTrim{ "malloc-trim", @@ -935,6 +940,7 @@ Opts.ClangTidyProvider = ClangTidyOptProvider; } Opts.UseDirtyHeaders = UseDirtyHeaders; + Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; Opts.QueryDriverGlobs = std::move(QueryDriverGlobs); Opts.TweakFilter = [&](const Tweak &T) { if (T.hidden() && !HiddenFeatures) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -121,8 +121,10 @@ ADD_FAILURE() << "Couldn't build CompilerInvocation"; return {}; } - auto Preamble = buildPreamble(testPath(TU.Filename), *CI, Inputs, - /*InMemory=*/true, /*Callback=*/nullptr); + auto Preamble = + buildPreamble(testPath(TU.Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/false, /*Callback=*/nullptr); return codeComplete(testPath(TU.Filename), Point, Preamble.get(), Inputs, Opts); } @@ -1191,8 +1193,10 @@ ADD_FAILURE() << "Couldn't build CompilerInvocation"; return {}; } - auto Preamble = buildPreamble(testPath(TU.Filename), *CI, Inputs, - /*InMemory=*/true, /*Callback=*/nullptr); + auto Preamble = + buildPreamble(testPath(TU.Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/false, /*Callback=*/nullptr); if (!Preamble) { ADD_FAILURE() << "Couldn't build Preamble"; return {}; @@ -1439,8 +1443,10 @@ auto Inputs = TU.inputs(FS); auto CI = buildCompilerInvocation(Inputs, Diags); ASSERT_TRUE(CI); - auto EmptyPreamble = buildPreamble(testPath(TU.Filename), *CI, Inputs, - /*InMemory=*/true, /*Callback=*/nullptr); + auto EmptyPreamble = + buildPreamble(testPath(TU.Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/false, /*Callback=*/nullptr); ASSERT_TRUE(EmptyPreamble); TU.AdditionalFiles["a.h"] = "int foo(int x);"; diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp --- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp @@ -306,7 +306,7 @@ FileIndex Index; bool IndexUpdated = false; buildPreamble(FooCpp, *CI, PI, - /*StoreInMemory=*/true, + /*StoreInMemory=*/true, /*ParseForwardingFunctions=*/false, [&](ASTContext &Ctx, Preprocessor &PP, const CanonicalIncludes &CanonIncludes) { EXPECT_FALSE(IndexUpdated) diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -497,7 +497,8 @@ auto Inputs = TU.inputs(FS); auto CI = buildCompilerInvocation(Inputs, Diags); auto EmptyPreamble = - buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr); + buildPreamble(testPath("foo.cpp"), *CI, Inputs, /*StoreInMemory=*/true, + /*PreambleCallback=*/false, nullptr); ASSERT_TRUE(EmptyPreamble); EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, IsEmpty()); @@ -540,7 +541,8 @@ auto Inputs = TU.inputs(FS); auto CI = buildCompilerInvocation(Inputs, Diags); auto BaselinePreamble = - buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr); + buildPreamble(testPath("foo.cpp"), *CI, Inputs, /*StoreInMemory=*/true, + /*PreambleCallback=*/false, nullptr); ASSERT_TRUE(BaselinePreamble); EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes, ElementsAre(testing::Field(&Inclusion::Written, ""))); diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp --- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -173,7 +173,8 @@ TU.AdditionalFiles["c.h"] = ""; auto PI = TU.inputs(FS); auto BaselinePreamble = buildPreamble( - TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr); + TU.Filename, *buildCompilerInvocation(PI, Diags), PI, + /*StoreInMemory=*/true, /*ParseForwardingFunctions=*/false, nullptr); // We drop c.h from modified and add a new header. Since the latter is patched // we should only get a.h in preamble includes. TU.Code = R"cpp( diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -101,7 +101,9 @@ auto ModuleCacheDeleter = llvm::make_scope_exit( std::bind(deleteModuleCache, CI->getHeaderSearchOpts().ModuleCachePath)); return clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, - /*StoreInMemory=*/true, PreambleCallback); + /*StoreInMemory=*/true, + /*ParseForwardingFunctions=*/false, + PreambleCallback); } ParsedAST TestTU::build() const { @@ -115,9 +117,10 @@ auto ModuleCacheDeleter = llvm::make_scope_exit( std::bind(deleteModuleCache, CI->getHeaderSearchOpts().ModuleCachePath)); - auto Preamble = clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, - /*StoreInMemory=*/true, - /*PreambleCallback=*/nullptr); + auto Preamble = + clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/false, nullptr); auto AST = ParsedAST::build(testPath(Filename), Inputs, std::move(CI), Diags.take(), Preamble); if (!AST.hasValue()) {