Author: timon-ul
Date: 2025-12-25T00:37:39-05:00
New Revision: 56f5fda5776a9693a79ccea4e23ae1295df81a69

URL: 
https://github.com/llvm/llvm-project/commit/56f5fda5776a9693a79ccea4e23ae1295df81a69
DIFF: 
https://github.com/llvm/llvm-project/commit/56f5fda5776a9693a79ccea4e23ae1295df81a69.diff

LOG: [clangd] Find references to constructors called indirectly via a 
forwarding function (#169742)

Calls to functions that forward to a constructor, such as make_unique,
are now recorded as references to the called constructor as well, so
that searching for references to a constructor finds such call sites.

Co-authored-by: Nathan Ridge <[email protected]>

Added: 
    

Modified: 
    clang-tools-extra/clangd/AST.cpp
    clang-tools-extra/clangd/AST.h
    clang-tools-extra/clangd/ParsedAST.h
    clang-tools-extra/clangd/Preamble.cpp
    clang-tools-extra/clangd/XRefs.cpp
    clang-tools-extra/clangd/index/IndexAction.cpp
    clang-tools-extra/clangd/index/SymbolCollector.cpp
    clang-tools-extra/clangd/index/SymbolCollector.h
    clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
    clang-tools-extra/clangd/unittests/XRefsTests.cpp
    clang/include/clang/Index/IndexingOptions.h
    clang/lib/Index/IndexingAction.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/AST.cpp 
b/clang-tools-extra/clangd/AST.cpp
index 0dcff2eae05e7..3bcc89d360cdb 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -18,7 +18,6 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
-#include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
@@ -31,8 +30,8 @@
 #include "clang/Index/USRGeneration.h"
 #include "clang/Sema/HeuristicResolver.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/raw_ostream.h"
@@ -1040,5 +1039,80 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D) {
   return getUnderlyingPackType(D) != nullptr;
 }
 
+bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT) {
+  const auto *FD = FT->getTemplatedDecl();
+  const auto NumParams = FD->getNumParams();
+  // Check whether its last parameter is a parameter pack...
+  if (NumParams > 0) {
+    const auto *LastParam = FD->getParamDecl(NumParams - 1);
+    if (const auto *PET = dyn_cast<PackExpansionType>(LastParam->getType())) {
+      // ... of the type T&&... or T...
+      const auto BaseType = PET->getPattern().getNonReferenceType();
+      if (const auto *TTPT =
+              dyn_cast<TemplateTypeParmType>(BaseType.getTypePtr())) {
+        // ... whose template parameter comes from the function directly
+        if (FT->getTemplateParameters()->getDepth() == TTPT->getDepth()) {
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+class ForwardingToConstructorVisitor
+    : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
+public:
+  ForwardingToConstructorVisitor(
+      llvm::DenseSet<const FunctionDecl *> &SeenFunctions,
+      SmallVector<const CXXConstructorDecl *, 1> &Output)
+      : SeenFunctions(SeenFunctions), Constructors(Output) {}
+
+  bool VisitCallExpr(CallExpr *E) {
+    // Adjust if recurison not deep enough
+    if (SeenFunctions.size() >= 10)
+      return true;
+    if (auto *FD = E->getDirectCallee()) {
+      // Check if we already visited this function to prevent endless recursion
+      if (SeenFunctions.contains(FD))
+        return true;
+      if (auto *PT = FD->getPrimaryTemplate();
+          PT && isLikelyForwardingFunction(PT)) {
+        SeenFunctions.insert(FD);
+        ForwardingToConstructorVisitor Visitor{SeenFunctions, Constructors};
+        Visitor.TraverseStmt(FD->getBody());
+        SeenFunctions.erase(FD);
+      }
+    }
+    return true;
+  }
+
+  bool VisitCXXNewExpr(CXXNewExpr *E) {
+    if (auto *CE = E->getConstructExpr())
+      if (auto *Callee = CE->getConstructor()) {
+        auto *Adjusted = &adjustDeclToTemplate(*Callee);
+        if (auto *Template = dyn_cast<TemplateDecl>(Adjusted))
+          Adjusted = Template->getTemplatedDecl();
+        if (auto *Constructor = dyn_cast<CXXConstructorDecl>(Adjusted))
+          Constructors.push_back(Constructor);
+      }
+    return true;
+  }
+
+  // Stack of seen functions
+  llvm::DenseSet<const FunctionDecl *> &SeenFunctions;
+  // Output of this visitor
+  SmallVector<const CXXConstructorDecl *, 1> &Constructors;
+};
+
+SmallVector<const CXXConstructorDecl *, 1>
+searchConstructorsInForwardingFunction(const FunctionDecl *FD) {
+  SmallVector<const CXXConstructorDecl *, 1> Result;
+  llvm::DenseSet<const FunctionDecl *> SeenFunctions{FD};
+  ForwardingToConstructorVisitor Visitor{SeenFunctions, Result};
+  Visitor.TraverseStmt(FD->getBody());
+  return Result;
+}
+
 } // namespace clangd
 } // namespace clang

diff  --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 2b83595e5b8e9..2bb4943b6de0b 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -18,7 +18,6 @@
 #include "index/SymbolID.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
-#include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Lex/MacroInfo.h"
@@ -253,6 +252,15 @@ resolveForwardingParameters(const FunctionDecl *D, 
unsigned MaxDepth = 10);
 /// reference to one (e.g. `Args&...` or `Args&&...`).
 bool isExpandedFromParameterPack(const ParmVarDecl *D);
 
+/// Heuristic that checks if FT is likely to be forwarding a parameter pack to
+/// another function (e.g. `make_unique`).
+bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT);
+
+/// Only call if FD is a likely forwarding function. Returns
+/// constructors that might be forwarded to.
+SmallVector<const CXXConstructorDecl *, 1>
+searchConstructorsInForwardingFunction(const FunctionDecl *FD);
+
 } // namespace clangd
 } // namespace clang
 

diff  --git a/clang-tools-extra/clangd/ParsedAST.h 
b/clang-tools-extra/clangd/ParsedAST.h
index 82fac96360488..6640ccccf5815 100644
--- a/clang-tools-extra/clangd/ParsedAST.h
+++ b/clang-tools-extra/clangd/ParsedAST.h
@@ -123,6 +123,11 @@ class ParsedAST {
     return Resolver.get();
   }
 
+  /// Cache for constructors called through forwarding, e.g. make_unique
+  llvm::DenseMap<const FunctionDecl *,
+                 SmallVector<const CXXConstructorDecl *, 1>>
+      ForwardingToConstructorCache;
+
 private:
   ParsedAST(PathRef TUPath, llvm::StringRef Version,
             std::shared_ptr<const PreambleData> Preamble,

diff  --git a/clang-tools-extra/clangd/Preamble.cpp 
b/clang-tools-extra/clangd/Preamble.cpp
index 8af9e4649218d..09aaf3290b585 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "Preamble.h"
+#include "AST.h"
 #include "CollectMacros.h"
 #include "Compiler.h"
 #include "Config.h"
@@ -166,27 +167,6 @@ class CppFilePreambleCallbacks : public PreambleCallbacks {
         collectPragmaMarksCallback(*SourceMgr, Marks));
   }
 
-  static bool isLikelyForwardingFunction(FunctionTemplateDecl *FT) {
-    const auto *FD = FT->getTemplatedDecl();
-    const auto NumParams = FD->getNumParams();
-    // Check whether its last parameter is a parameter pack...
-    if (NumParams > 0) {
-      const auto *LastParam = FD->getParamDecl(NumParams - 1);
-      if (const auto *PET = dyn_cast<PackExpansionType>(LastParam->getType())) 
{
-        // ... of the type T&&... or T...
-        const auto BaseType = PET->getPattern().getNonReferenceType();
-        if (const auto *TTPT =
-                dyn_cast<TemplateTypeParmType>(BaseType.getTypePtr())) {
-          // ... whose template parameter comes from the function directly
-          if (FT->getTemplateParameters()->getDepth() == TTPT->getDepth()) {
-            return true;
-          }
-        }
-      }
-    }
-    return false;
-  }
-
   bool shouldSkipFunctionBody(Decl *D) override {
     // Usually we don't need to look inside the bodies of header functions
     // to understand the program. However when forwarding function like

diff  --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index ef45acf501612..4e330bf769d20 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -16,7 +16,6 @@
 #include "Quality.h"
 #include "Selection.h"
 #include "SourceCode.h"
-#include "URI.h"
 #include "clang-include-cleaner/Analysis.h"
 #include "clang-include-cleaner/Types.h"
 #include "index/Index.h"
@@ -43,7 +42,6 @@
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/LLVM.h"
-#include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TokenKinds.h"
@@ -60,7 +58,6 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
-#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
@@ -929,12 +926,15 @@ class ReferenceFinder : public index::IndexDataConsumer {
     }
   };
 
-  ReferenceFinder(const ParsedAST &AST,
+  ReferenceFinder(ParsedAST &AST,
                   const llvm::ArrayRef<const NamedDecl *> Targets,
                   bool PerToken)
       : PerToken(PerToken), AST(AST) {
-    for (const NamedDecl *ND : Targets)
+    for (const NamedDecl *ND : Targets) {
       TargetDecls.insert(ND->getCanonicalDecl());
+      if (auto *Constructor = llvm::dyn_cast<clang::CXXConstructorDecl>(ND))
+        TargetConstructors.insert(Constructor);
+    }
   }
 
   std::vector<Reference> take() && {
@@ -955,12 +955,41 @@ class ReferenceFinder : public index::IndexDataConsumer {
     return std::move(References);
   }
 
+  bool forwardsToConstructor(const Decl *D) {
+    if (TargetConstructors.empty())
+      return false;
+    auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+    if (FD == nullptr || !FD->isTemplateInstantiation())
+      return false;
+
+    SmallVector<const CXXConstructorDecl *, 1> *Constructors = nullptr;
+    if (auto Entry = AST.ForwardingToConstructorCache.find(FD);
+        Entry != AST.ForwardingToConstructorCache.end())
+      Constructors = &Entry->getSecond();
+    if (Constructors == nullptr) {
+      if (auto *PT = FD->getPrimaryTemplate();
+          PT == nullptr || !isLikelyForwardingFunction(PT))
+        return false;
+
+      SmallVector<const CXXConstructorDecl *, 1> FoundConstructors =
+          searchConstructorsInForwardingFunction(FD);
+      auto Iter = AST.ForwardingToConstructorCache.try_emplace(
+          FD, std::move(FoundConstructors));
+      Constructors = &Iter.first->getSecond();
+    }
+    for (auto *Constructor : *Constructors)
+      if (TargetConstructors.contains(Constructor))
+        return true;
+    return false;
+  }
+
   bool
   handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles,
                        llvm::ArrayRef<index::SymbolRelation> Relations,
                        SourceLocation Loc,
                        index::IndexDataConsumer::ASTNodeInfo ASTNode) override 
{
-    if (!TargetDecls.contains(D->getCanonicalDecl()))
+    if (!TargetDecls.contains(D->getCanonicalDecl()) &&
+        !forwardsToConstructor(ASTNode.OrigD))
       return true;
     const SourceManager &SM = AST.getSourceManager();
     if (!isInsideMainFile(Loc, SM))
@@ -1000,8 +1029,10 @@ class ReferenceFinder : public index::IndexDataConsumer {
 private:
   bool PerToken; // If true, report 3 references for split ObjC selector names.
   std::vector<Reference> References;
-  const ParsedAST &AST;
+  ParsedAST &AST;
   llvm::DenseSet<const Decl *> TargetDecls;
+  // Constructors need special handling since they can be hidden behind 
forwards
+  llvm::DenseSet<const CXXConstructorDecl *> TargetConstructors;
 };
 
 std::vector<ReferenceFinder::Reference>

diff  --git a/clang-tools-extra/clangd/index/IndexAction.cpp 
b/clang-tools-extra/clangd/index/IndexAction.cpp
index ed56c2a9d2e81..09943400f6d86 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -21,7 +21,6 @@
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Index/IndexingAction.h"
 #include "clang/Index/IndexingOptions.h"
-#include <cstddef>
 #include <functional>
 #include <memory>
 #include <optional>
@@ -146,6 +145,11 @@ class IndexAction : public ASTFrontendAction {
       // inside, it becomes quadratic. So we give up on nested symbols.
       if (isDeeplyNested(D))
         return false;
+      // If D is a likely forwarding function we need the body to index 
indirect
+      // constructor calls (e.g. `make_unique`)
+      if (auto *FT = llvm::dyn_cast<clang::FunctionTemplateDecl>(D);
+          FT && isLikelyForwardingFunction(FT))
+        return true;
       auto &SM = D->getASTContext().getSourceManager();
       auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
       if (!FID.isValid())
@@ -220,6 +224,9 @@ std::unique_ptr<FrontendAction> createStaticIndexingAction(
       index::IndexingOptions::SystemSymbolFilterKind::All;
   // We index function-local classes and its member functions only.
   IndexOpts.IndexFunctionLocals = true;
+  // We need to delay indexing so instantiations of function bodies become
+  // available, this is so we can find constructor calls through `make_unique`.
+  IndexOpts.DeferIndexingToEndOfTranslationUnit = true;
   Opts.CollectIncludePath = true;
   if (Opts.Origin == SymbolOrigin::Unknown)
     Opts.Origin = SymbolOrigin::Static;

diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 39c479b5f4d5b..bd974e4c18818 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -25,6 +25,7 @@
 #include "index/SymbolLocation.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
@@ -44,7 +45,6 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include <cassert>
 #include <memory>
@@ -576,6 +576,25 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
   return Enclosing;
 }
 
+SmallVector<const CXXConstructorDecl *, 1>
+SymbolCollector::findIndirectConstructors(const Decl *D) {
+  auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+  if (FD == nullptr || !FD->isTemplateInstantiation())
+    return {};
+  if (auto Entry = ForwardingToConstructorCache.find(FD);
+      Entry != ForwardingToConstructorCache.end())
+    return Entry->getSecond();
+  if (auto *PT = FD->getPrimaryTemplate();
+      PT == nullptr || !isLikelyForwardingFunction(PT))
+    return {};
+
+  SmallVector<const CXXConstructorDecl *, 1> FoundConstructors =
+      searchConstructorsInForwardingFunction(FD);
+  auto Iter = ForwardingToConstructorCache.try_emplace(
+      FD, std::move(FoundConstructors));
+  return Iter.first->getSecond();
+}
+
 // Always return true to continue indexing.
 bool SymbolCollector::handleDeclOccurrence(
     const Decl *D, index::SymbolRoleSet Roles,
@@ -639,10 +658,12 @@ bool SymbolCollector::handleDeclOccurrence(
   // ND is the canonical (i.e. first) declaration. If it's in the main file
   // (which is not a header), then no public declaration was visible, so assume
   // it's main-file only.
-  bool IsMainFileOnly =
-      SM.isWrittenInMainFile(SM.getExpansionLoc(ND->getBeginLoc())) &&
-      !isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
-                    ASTCtx->getLangOpts());
+  auto CheckIsMainFileOnly = [&](const NamedDecl *Decl) {
+    return SM.isWrittenInMainFile(SM.getExpansionLoc(Decl->getBeginLoc())) &&
+           
!isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
+                         ASTCtx->getLangOpts());
+  };
+  bool IsMainFileOnly = CheckIsMainFileOnly(ND);
   // In C, printf is a redecl of an implicit builtin! So check OrigD instead.
   if (ASTNode.OrigD->isImplicit() ||
       !shouldCollectSymbol(*ND, *ASTCtx, Opts, IsMainFileOnly))
@@ -666,9 +687,20 @@ bool SymbolCollector::handleDeclOccurrence(
     auto FileLoc = SM.getFileLoc(Loc);
     auto FID = SM.getFileID(FileLoc);
     if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
+      auto *Container = getRefContainer(ASTNode.Parent, Opts);
       addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
-                           getRefContainer(ASTNode.Parent, Opts),
-                           isSpelled(FileLoc, *ND)});
+                           Container, isSpelled(FileLoc, *ND)});
+      // Also collect indirect constructor calls like `make_unique`
+      for (auto *Constructor : findIndirectConstructors(ASTNode.OrigD)) {
+        if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts,
+                                 CheckIsMainFileOnly(Constructor)))
+          continue;
+        if (auto ConstructorID = getSymbolIDCached(Constructor))
+          addRef(ConstructorID,
+                 SymbolRef{FileLoc, FID, Roles,
+                           index::getSymbolInfo(Constructor).Kind, Container,
+                           false});
+      }
     }
   }
   // Don't continue indexing if this is a mere reference.

diff  --git a/clang-tools-extra/clangd/index/SymbolCollector.h 
b/clang-tools-extra/clangd/index/SymbolCollector.h
index e9eb27fd0f664..4d51d747639b1 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -159,6 +159,12 @@ class SymbolCollector : public index::IndexDataConsumer {
   void finish() override;
 
 private:
+  // If D is an instantiation of a likely forwarding function, return the
+  // constructors it invokes so that we can record indirect references
+  // to those as well.
+  SmallVector<const CXXConstructorDecl *, 1>
+  findIndirectConstructors(const Decl *D);
+
   const Symbol *addDeclaration(const NamedDecl &, SymbolID,
                                bool IsMainFileSymbol);
   void addDefinition(const NamedDecl &, const Symbol &DeclSymbol,
@@ -230,6 +236,9 @@ class SymbolCollector : public index::IndexDataConsumer {
   std::unique_ptr<HeaderFileURICache> HeaderFileURIs;
   llvm::DenseMap<const Decl *, SymbolID> DeclToIDCache;
   llvm::DenseMap<const MacroInfo *, SymbolID> MacroToIDCache;
+  llvm::DenseMap<const FunctionDecl *,
+                 SmallVector<const CXXConstructorDecl *, 1>>
+      ForwardingToConstructorCache;
 };
 
 } // namespace clangd

diff  --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp 
b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
index ada14c9939318..0eb4acf0469b7 100644
--- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
+++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
@@ -1,3 +1,4 @@
+#include "Annotations.h"
 #include "CompileCommands.h"
 #include "Config.h"
 #include "Headers.h"
@@ -14,7 +15,6 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <deque>
-#include <thread>
 
 using ::testing::_;
 using ::testing::AllOf;
@@ -233,6 +233,126 @@ TEST_F(BackgroundIndexTest, IndexTwoFiles) {
                        fileURI("unittest:///root/B.cc")}));
 }
 
+TEST_F(BackgroundIndexTest, ConstructorForwarding) {
+  Annotations Header(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+    }
+    struct Test {
+      [[Test]](){}
+    };
+  )cpp");
+  Annotations Main(R"cpp(
+    #include "header.hpp"
+    int main() {
+      auto a = std::[[make_unique]]<Test>();
+    }
+  )cpp");
+
+  MockFS FS;
+  llvm::StringMap<std::string> Storage;
+  size_t CacheHits = 0;
+  MemoryShardStorage MSS(Storage, CacheHits);
+  OverlayCDB CDB(/*Base=*/nullptr);
+  BackgroundIndex::Options Opts;
+  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
+
+  FS.Files[testPath("root/header.hpp")] = Header.code();
+  FS.Files[testPath("root/test.cpp")] = Main.code();
+
+  tooling::CompileCommand Cmd;
+  Cmd.Filename = testPath("root/test.cpp");
+  Cmd.Directory = testPath("root");
+  Cmd.CommandLine = {"clang++", testPath("root/test.cpp")};
+  CDB.setCompileCommand(testPath("root/test.cpp"), Cmd);
+
+  ASSERT_TRUE(Idx.blockUntilIdleForTest());
+
+  auto Syms = runFuzzyFind(Idx, "Test");
+  auto Constructor =
+      std::find_if(Syms.begin(), Syms.end(), [](const Symbol &S) {
+        return S.SymInfo.Kind == index::SymbolKind::Constructor;
+      });
+  ASSERT_TRUE(Constructor != Syms.end());
+  EXPECT_THAT(getRefs(Idx, Constructor->ID),
+              refsAre({fileURI("unittest:///root/header.hpp"),
+                       fileURI("unittest:///root/test.cpp")}));
+}
+
+TEST_F(BackgroundIndexTest, ConstructorForwardingMultiFile) {
+  // If a forwarding function like `make_unique` is defined in a header its 
body
+  // used to be skipped on the second encounter. This meant in practise we 
could
+  // only find constructors indirectly called by these type of functions in the
+  // first indexed file (and all files that were indexed at the same time,
+  // before a flag to skip it was set).
+  Annotations Header(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+    }
+    struct Test {
+      [[Test]](){}
+    };
+  )cpp");
+  Annotations First(R"cpp(
+    #include "header.hpp"
+    int main() {
+      auto a = std::[[make_unique]]<Test>();
+    }
+  )cpp");
+  Annotations Second(R"cpp(
+    #include "header.hpp"
+    void test() {
+      auto a = std::[[make_unique]]<Test>();
+    }
+  )cpp");
+
+  MockFS FS;
+  llvm::StringMap<std::string> Storage;
+  size_t CacheHits = 0;
+  MemoryShardStorage MSS(Storage, CacheHits);
+  OverlayCDB CDB(/*Base=*/nullptr);
+  BackgroundIndex::Options Opts;
+  BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts);
+
+  FS.Files[testPath("root/header.hpp")] = Header.code();
+  FS.Files[testPath("root/first.cpp")] = First.code();
+  FS.Files[testPath("root/second.cpp")] = Second.code();
+
+  tooling::CompileCommand Cmd;
+  Cmd.Filename = testPath("root/first.cpp");
+  Cmd.Directory = testPath("root");
+  Cmd.CommandLine = {"clang++", testPath("root/first.cpp")};
+  CDB.setCompileCommand(testPath("root/first.cpp"), Cmd);
+
+  // Make sure the first file is done indexing to make sure the flag for the
+  // header is set.
+  ASSERT_TRUE(Idx.blockUntilIdleForTest());
+
+  Cmd.Filename = testPath("root/second.cpp");
+  Cmd.Directory = testPath("root");
+  Cmd.CommandLine = {"clang++", testPath("root/second.cpp")};
+  CDB.setCompileCommand(testPath("root/second.cpp"), Cmd);
+
+  ASSERT_TRUE(Idx.blockUntilIdleForTest());
+
+  auto Syms = runFuzzyFind(Idx, "Test");
+  auto Constructor =
+      std::find_if(Syms.begin(), Syms.end(), [](const Symbol &S) {
+        return S.SymInfo.Kind == index::SymbolKind::Constructor;
+      });
+  ASSERT_TRUE(Constructor != Syms.end());
+  EXPECT_THAT(getRefs(Idx, Constructor->ID),
+              refsAre({fileURI("unittest:///root/header.hpp"),
+                       fileURI("unittest:///root/first.cpp"),
+                       fileURI("unittest:///root/second.cpp")}));
+}
+
 TEST_F(BackgroundIndexTest, MainFileRefs) {
   MockFS FS;
   FS.Files[testPath("root/A.h")] = R"cpp(

diff  --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 7ed08d7cce3d3..4106c6cf7b2d0 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -5,14 +5,15 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 
//===----------------------------------------------------------------------===//
-#include "Annotations.h"
 #include "AST.h"
+#include "Annotations.h"
 #include "ParsedAST.h"
 #include "Protocol.h"
 #include "SourceCode.h"
 #include "SyncAPI.h"
 #include "TestFS.h"
 #include "TestTU.h"
+#include "TestWorkspace.h"
 #include "XRefs.h"
 #include "index/MemIndex.h"
 #include "clang/AST/Decl.h"
@@ -311,6 +312,7 @@ MATCHER_P3(sym, Name, Decl, DefOrNone, "") {
 MATCHER_P(sym, Name, "") { return arg.Name == Name; }
 
 MATCHER_P(rangeIs, R, "") { return arg.Loc.range == R; }
+MATCHER_P(fileIs, F, "") { return arg.Loc.uri.file() == F; }
 MATCHER_P(containerIs, C, "") {
   return arg.Loc.containerName.value_or("") == C;
 }
@@ -2713,6 +2715,140 @@ TEST(FindReferences, NoQueryForLocalSymbols) {
   }
 }
 
+TEST(FindReferences, ConstructorForwardingInAST) {
+  Annotations Main(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+    }
+
+    struct Test {
+      $Constructor[[T^est]](){}
+    };
+
+    int main() {
+      auto a = std::$Caller[[make_unique]]<Test>();
+    }
+  )cpp");
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+
+  EXPECT_THAT(findReferences(AST, Main.point(), 0).References,
+              ElementsAre(rangeIs(Main.range("Constructor")),
+                          rangeIs(Main.range("Caller"))));
+}
+
+TEST(FindReferences, ConstructorForwardingInASTChained) {
+  Annotations Main(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(forward<Args>(args)...);
+    }
+    template <class T, class... Args> T *make_unique2(Args &&...args) {
+      return make_unique<T>(forward<Args>(args)...);
+    }
+    template <class T, class... Args> T *make_unique3(Args &&...args) {
+      return make_unique2<T>(forward<Args>(args)...);
+    }
+    }
+
+    struct Test {
+      $Constructor[[T^est]](){}
+    };
+
+    int main() {
+      auto a = std::$Caller[[make_unique3]]<Test>();
+    }
+  )cpp");
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+
+  EXPECT_THAT(findReferences(AST, Main.point(), 0).References,
+              ElementsAre(rangeIs(Main.range("Constructor")),
+                          rangeIs(Main.range("Caller"))));
+}
+
+TEST(FindReferences, ConstructorForwardingInIndex) {
+  Annotations Header(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+    }
+    struct Test {
+      [[T^est]](){}
+    };
+  )cpp");
+  Annotations Main(R"cpp(
+    #include "header.hpp"
+    int main() {
+      auto a = std::[[make_unique]]<Test>();
+    }
+  )cpp");
+  TestWorkspace TW;
+  TW.addSource("header.hpp", Header.code());
+  TW.addMainFile("main.cpp", Main.code());
+  auto AST = TW.openFile("header.hpp").value();
+  auto Index = TW.index();
+
+  EXPECT_THAT(
+      findReferences(AST, Header.point(), 0, Index.get(),
+                     /*AddContext*/ true)
+          .References,
+      ElementsAre(
+          AllOf(rangeIs(Header.range()), fileIs(testPath("header.hpp"))),
+          AllOf(rangeIs(Main.range()), fileIs(testPath("main.cpp")))));
+}
+
+TEST(FindReferences, TemplatedConstructorForwarding) {
+  Annotations Main(R"cpp(
+    namespace std {
+    template <class T> T &&forward(T &t);
+    template <class T, class... Args> T *make_unique(Args &&...args) {
+      return new T(std::forward<Args>(args)...);
+    }
+    }
+
+    struct Waldo {
+      template <typename T>
+      $Constructor[[W$Waldo^aldo]](T);
+    };
+    template <typename T>
+    struct Waldo2 {
+      $Constructor2[[W$Waldo2^aldo2]](int);
+    };
+    struct S {};
+
+    int main() {
+      S s;
+      Waldo $Caller[[w]](s);
+      std::$ForwardedCaller[[make_unique]]<Waldo>(s);
+
+      Waldo2<int> $Caller2[[w2]](42);
+      std::$ForwardedCaller2[[make_unique]]<Waldo2<int>>(42);
+    }
+  )cpp");
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+
+  EXPECT_THAT(findReferences(AST, Main.point("Waldo"), 0).References,
+              ElementsAre(rangeIs(Main.range("Constructor")),
+                          rangeIs(Main.range("Caller")),
+                          rangeIs(Main.range("ForwardedCaller"))));
+
+  EXPECT_THAT(findReferences(AST, Main.point("Waldo2"), 0).References,
+              ElementsAre(rangeIs(Main.range("Constructor2")),
+                          rangeIs(Main.range("Caller2")),
+                          rangeIs(Main.range("ForwardedCaller2"))));
+}
+
 TEST(GetNonLocalDeclRefs, All) {
   struct Case {
     llvm::StringRef AnnotatedCode;

diff  --git a/clang/include/clang/Index/IndexingOptions.h 
b/clang/include/clang/Index/IndexingOptions.h
index 97847dd7d5d88..c670797e9fa60 100644
--- a/clang/include/clang/Index/IndexingOptions.h
+++ b/clang/include/clang/Index/IndexingOptions.h
@@ -36,6 +36,12 @@ struct IndexingOptions {
   // Has no effect if IndexFunctionLocals are false.
   bool IndexParametersInDeclarations = false;
   bool IndexTemplateParameters = false;
+  // Some information might only be available at the end of a translation unit,
+  // this flag delays the indexing for this purpose (e.g. instantiation of
+  // function definitions). This option only takes effect on operations that
+  // actually build the AST, e.g. `createIndexingAction()` and
+  // `createIndexingASTConsumer()`.
+  bool DeferIndexingToEndOfTranslationUnit = false;
 
   // If set, skip indexing inside some declarations for performance.
   // This prevents traversal, so skipping a struct means its declaration an

diff  --git a/clang/lib/Index/IndexingAction.cpp 
b/clang/lib/Index/IndexingAction.cpp
index 73a6a8c62af2e..8118ceda9cd23 100644
--- a/clang/lib/Index/IndexingAction.cpp
+++ b/clang/lib/Index/IndexingAction.cpp
@@ -8,6 +8,7 @@
 
 #include "clang/Index/IndexingAction.h"
 #include "IndexingContext.h"
+#include "clang/AST/DeclGroup.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Index/IndexDataConsumer.h"
@@ -101,6 +102,7 @@ class IndexASTConsumer final : public ASTConsumer {
   std::shared_ptr<IndexingContext> IndexCtx;
   std::shared_ptr<Preprocessor> PP;
   std::function<bool(const Decl *)> ShouldSkipFunctionBody;
+  bool DeferIndexingToEndOfTranslationUnit;
 
 public:
   IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer,
@@ -110,7 +112,9 @@ class IndexASTConsumer final : public ASTConsumer {
       : DataConsumer(std::move(DataConsumer)),
         IndexCtx(new IndexingContext(Opts, *this->DataConsumer)),
         PP(std::move(PP)),
-        ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)) {
+        ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)),
+        DeferIndexingToEndOfTranslationUnit(
+            Opts.DeferIndexingToEndOfTranslationUnit) {
     assert(this->DataConsumer != nullptr);
     assert(this->PP != nullptr);
   }
@@ -124,7 +128,9 @@ class IndexASTConsumer final : public ASTConsumer {
   }
 
   bool HandleTopLevelDecl(DeclGroupRef DG) override {
-    return IndexCtx->indexDeclGroupRef(DG);
+    if (!DeferIndexingToEndOfTranslationUnit)
+      return IndexCtx->indexDeclGroupRef(DG);
+    return true;
   }
 
   void HandleInterestingDecl(DeclGroupRef DG) override {
@@ -132,10 +138,14 @@ class IndexASTConsumer final : public ASTConsumer {
   }
 
   void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
-    IndexCtx->indexDeclGroupRef(DG);
+    if (!DeferIndexingToEndOfTranslationUnit)
+      IndexCtx->indexDeclGroupRef(DG);
   }
 
   void HandleTranslationUnit(ASTContext &Ctx) override {
+    if (DeferIndexingToEndOfTranslationUnit)
+      for (auto *DG : Ctx.getTranslationUnitDecl()->decls())
+        IndexCtx->indexTopLevelDecl(DG);
     DataConsumer->finish();
   }
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to