[clang] [clang-tools-extra] clangd: Extend reference search with constructor calls through forwarding (PR #169742)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 23 09:46:39 PST 2025
https://github.com/timon-ul updated https://github.com/llvm/llvm-project/pull/169742
>From 44ebad6933fccab5bcfc866ee56dd5381da5f452 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Mon, 10 Nov 2025 10:40:14 +0100
Subject: [PATCH 01/22] clangd: make forwarding heuristic available for more
locations
---
clang-tools-extra/clangd/AST.cpp | 21 +++++++++++++++++++++
clang-tools-extra/clangd/AST.h | 4 ++++
clang-tools-extra/clangd/Preamble.cpp | 22 +---------------------
3 files changed, 26 insertions(+), 21 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 0dcff2eae05e7..4b73bdba8aaa9 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1040,5 +1040,26 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D) {
return getUnderlyingPackType(D) != nullptr;
}
+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;
+}
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 2b83595e5b8e9..af45ae2d9022d 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -253,6 +253,10 @@ 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 forwarding a parameter pack to another
+/// function. (e.g. `make_unique`).
+bool isLikelyForwardingFunction(FunctionTemplateDecl *FT);
+
} // namespace clangd
} // namespace clang
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
>From f0d886eecf613bbf913629acd8951ee9ac8ab242 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Tue, 18 Nov 2025 14:07:14 +0100
Subject: [PATCH 02/22] clangd: Added new unittest for finding more refs to
constructors
Constructor calls hidden behind make_unique are currently not found, but
very useful, this test expects to find them in the main index.
---
.../clangd/unittests/XRefsTests.cpp | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 7ed08d7cce3d3..51e29f5f1af43 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -13,6 +13,7 @@
#include "SyncAPI.h"
#include "TestFS.h"
#include "TestTU.h"
+#include "TestWorkspace.h"
#include "XRefs.h"
#include "index/MemIndex.h"
#include "clang/AST/Decl.h"
@@ -2713,6 +2714,36 @@ TEST(FindReferences, NoQueryForLocalSymbols) {
}
}
+TEST(FindReferences, ForwardingInIndex) {
+ 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.addMainFile("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(rangeIs(Header.range()), rangeIs(Main.range())));
+}
+
TEST(GetNonLocalDeclRefs, All) {
struct Case {
llvm::StringRef AnnotatedCode;
>From 36972c5dab08029a3fc71e7730d26c5df4333cd3 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 26 Nov 2025 23:38:11 +0100
Subject: [PATCH 03/22] clangd: Collecting constructor references through
forwarding in index
---
.../clangd/index/SymbolCollector.cpp | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 39c479b5f4d5b..aed74ddac1f46 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -29,6 +29,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
@@ -576,6 +577,21 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
+class ForwardVisitor : public RecursiveASTVisitor<ForwardVisitor> {
+public:
+ ForwardVisitor() {}
+
+ bool VisitCXXConstructExpr(CXXConstructExpr *E) {
+ if (auto *Callee = E->getConstructor()) {
+ Constructors.push_back(Callee);
+ }
+ return true;
+ }
+
+ // Output of this visitor
+ std::vector<CXXConstructorDecl *> Constructors{};
+};
+
// Always return true to continue indexing.
bool SymbolCollector::handleDeclOccurrence(
const Decl *D, index::SymbolRoleSet Roles,
@@ -654,6 +670,26 @@ bool SymbolCollector::handleDeclOccurrence(
// occurrence inside the base-specifier.
processRelations(*ND, ID, Relations);
+ if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
+ if (auto *FT = FD->getDescribedFunctionTemplate();
+ FT && isLikelyForwardingFunction(FT)) {
+ ForwardVisitor FS{};
+ for (auto *Specialized : FT->specializations()) {
+ FS.TraverseStmt(Specialized->getBody());
+ }
+ auto FileLoc = SM.getFileLoc(Loc);
+ auto FID = SM.getFileID(FileLoc);
+ if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
+ for (auto *Constructor : FS.Constructors) {
+ addRef(getSymbolIDCached(Constructor),
+ SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
+ getRefContainer(ASTNode.Parent, Opts),
+ isSpelled(FileLoc, *ND)});
+ }
+ }
+ }
+ }
+
bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
// Unlike other fields, e.g. Symbols (which use spelling locations), we use
// file locations for references (as it aligns the behavior of clangd's
>From 52a924d3bff04c64a06252662b16bc1b51c1582e Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 26 Nov 2025 23:48:15 +0100
Subject: [PATCH 04/22] clangd: rename new visitor to better reflect task
---
clang-tools-extra/clangd/index/SymbolCollector.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index aed74ddac1f46..f08e753eadda5 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -577,9 +577,10 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
-class ForwardVisitor : public RecursiveASTVisitor<ForwardVisitor> {
+class ForwardingToConstructorVisitor
+ : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
public:
- ForwardVisitor() {}
+ ForwardingToConstructorVisitor() {}
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
if (auto *Callee = E->getConstructor()) {
@@ -673,14 +674,14 @@ bool SymbolCollector::handleDeclOccurrence(
if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
if (auto *FT = FD->getDescribedFunctionTemplate();
FT && isLikelyForwardingFunction(FT)) {
- ForwardVisitor FS{};
+ ForwardingToConstructorVisitor Visitor{};
for (auto *Specialized : FT->specializations()) {
- FS.TraverseStmt(Specialized->getBody());
+ Visitor.TraverseStmt(Specialized->getBody());
}
auto FileLoc = SM.getFileLoc(Loc);
auto FID = SM.getFileID(FileLoc);
if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
- for (auto *Constructor : FS.Constructors) {
+ for (auto *Constructor : Visitor.Constructors) {
addRef(getSymbolIDCached(Constructor),
SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
getRefContainer(ASTNode.Parent, Opts),
>From 8a534aa88ee1d33158c3efe37139056bf4fba0b7 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Thu, 27 Nov 2025 00:03:10 +0100
Subject: [PATCH 05/22] clangd: moving new ref collection to correct location
---
.../clangd/index/SymbolCollector.cpp | 41 ++++++++++---------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index f08e753eadda5..11afd16e5f99d 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -671,26 +671,6 @@ bool SymbolCollector::handleDeclOccurrence(
// occurrence inside the base-specifier.
processRelations(*ND, ID, Relations);
- if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
- if (auto *FT = FD->getDescribedFunctionTemplate();
- FT && isLikelyForwardingFunction(FT)) {
- ForwardingToConstructorVisitor Visitor{};
- for (auto *Specialized : FT->specializations()) {
- Visitor.TraverseStmt(Specialized->getBody());
- }
- auto FileLoc = SM.getFileLoc(Loc);
- auto FID = SM.getFileID(FileLoc);
- if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
- for (auto *Constructor : Visitor.Constructors) {
- addRef(getSymbolIDCached(Constructor),
- SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
- getRefContainer(ASTNode.Parent, Opts),
- isSpelled(FileLoc, *ND)});
- }
- }
- }
- }
-
bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
// Unlike other fields, e.g. Symbols (which use spelling locations), we use
// file locations for references (as it aligns the behavior of clangd's
@@ -706,6 +686,27 @@ bool SymbolCollector::handleDeclOccurrence(
addRef(ID, SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
getRefContainer(ASTNode.Parent, Opts),
isSpelled(FileLoc, *ND)});
+ // Also collect indirect constructor calls like `make_unique`
+ if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
+ if (auto *FT = FD->getDescribedFunctionTemplate();
+ FT && isLikelyForwardingFunction(FT)) {
+ ForwardingToConstructorVisitor Visitor{};
+ for (auto *Specialized : FT->specializations()) {
+ Visitor.TraverseStmt(Specialized->getBody());
+ }
+ auto FileLoc = SM.getFileLoc(Loc);
+ auto FID = SM.getFileID(FileLoc);
+ if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
+ for (auto *Constructor : Visitor.Constructors) {
+ addRef(getSymbolIDCached(Constructor),
+ SymbolRef{FileLoc, FID, Roles,
+ index::getSymbolInfo(ND).Kind,
+ getRefContainer(ASTNode.Parent, Opts),
+ isSpelled(FileLoc, *ND)});
+ }
+ }
+ }
+ }
}
}
// Don't continue indexing if this is a mere reference.
>From 484ba5620270ad3bb3dd14ae07997a7cf8c057aa Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Thu, 27 Nov 2025 00:13:39 +0100
Subject: [PATCH 06/22] clangd: test include formatting
---
clang-tools-extra/clangd/unittests/XRefsTests.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 51e29f5f1af43..d3b6faa5fa25f 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -5,8 +5,8 @@
// 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"
>From 8c6928c3893abc2d023bc40fa4ca0c6616a9498a Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Thu, 27 Nov 2025 00:39:32 +0100
Subject: [PATCH 07/22] clangd: removed redundant code
---
.../clangd/index/SymbolCollector.cpp | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 11afd16e5f99d..54e4333870978 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -694,16 +694,11 @@ bool SymbolCollector::handleDeclOccurrence(
for (auto *Specialized : FT->specializations()) {
Visitor.TraverseStmt(Specialized->getBody());
}
- auto FileLoc = SM.getFileLoc(Loc);
- auto FID = SM.getFileID(FileLoc);
- if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
- for (auto *Constructor : Visitor.Constructors) {
- addRef(getSymbolIDCached(Constructor),
- SymbolRef{FileLoc, FID, Roles,
- index::getSymbolInfo(ND).Kind,
- getRefContainer(ASTNode.Parent, Opts),
- isSpelled(FileLoc, *ND)});
- }
+ for (auto *Constructor : Visitor.Constructors) {
+ addRef(getSymbolIDCached(Constructor),
+ SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
+ getRefContainer(ASTNode.Parent, Opts),
+ isSpelled(FileLoc, *ND)});
}
}
}
>From 7f4693cba8616a6870ad78bb7a85f595fc4f953c Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Fri, 28 Nov 2025 19:31:58 +0100
Subject: [PATCH 08/22] clangd: draft commit, trying different direction
idea is to directly at the instantiation index the template for
constructor calls. For some reason the function declaration never is an
instantiation though.
---
.../clangd/index/SymbolCollector.cpp | 23 ++++++++++---------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 54e4333870978..2b5f4541d6fa7 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -688,17 +688,18 @@ bool SymbolCollector::handleDeclOccurrence(
isSpelled(FileLoc, *ND)});
// Also collect indirect constructor calls like `make_unique`
if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
- if (auto *FT = FD->getDescribedFunctionTemplate();
- FT && isLikelyForwardingFunction(FT)) {
- ForwardingToConstructorVisitor Visitor{};
- for (auto *Specialized : FT->specializations()) {
- Visitor.TraverseStmt(Specialized->getBody());
- }
- for (auto *Constructor : Visitor.Constructors) {
- addRef(getSymbolIDCached(Constructor),
- SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
- getRefContainer(ASTNode.Parent, Opts),
- isSpelled(FileLoc, *ND)});
+ if (FD->isTemplateInstantiation()) {
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunction(PT)) {
+ ForwardingToConstructorVisitor Visitor{};
+ Visitor.TraverseStmt(FD->getBody());
+ for (auto *Constructor : Visitor.Constructors) {
+ addRef(getSymbolIDCached(Constructor),
+ SymbolRef{FileLoc, FID, Roles,
+ index::getSymbolInfo(ND).Kind,
+ getRefContainer(ASTNode.Parent, Opts),
+ isSpelled(FileLoc, *ND)});
+ }
}
}
}
>From 03e231268eaaf20c7bd6e640ec368762bb2ad407 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Sat, 29 Nov 2025 22:34:45 +0100
Subject: [PATCH 09/22] clangd: Fixed using correct declaration for indexing
constructors
---
.../clangd/index/SymbolCollector.cpp | 24 +++++++++----------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 2b5f4541d6fa7..bc5ebbef31fbd 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -687,19 +687,17 @@ bool SymbolCollector::handleDeclOccurrence(
getRefContainer(ASTNode.Parent, Opts),
isSpelled(FileLoc, *ND)});
// Also collect indirect constructor calls like `make_unique`
- if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D)) {
- if (FD->isTemplateInstantiation()) {
- if (auto *PT = FD->getPrimaryTemplate();
- PT && isLikelyForwardingFunction(PT)) {
- ForwardingToConstructorVisitor Visitor{};
- Visitor.TraverseStmt(FD->getBody());
- for (auto *Constructor : Visitor.Constructors) {
- addRef(getSymbolIDCached(Constructor),
- SymbolRef{FileLoc, FID, Roles,
- index::getSymbolInfo(ND).Kind,
- getRefContainer(ASTNode.Parent, Opts),
- isSpelled(FileLoc, *ND)});
- }
+ if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(ASTNode.OrigD);
+ FD && FD->isTemplateInstantiation()) {
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunction(PT)) {
+ ForwardingToConstructorVisitor Visitor{};
+ Visitor.TraverseStmt(FD->getBody());
+ for (auto *Constructor : Visitor.Constructors) {
+ addRef(getSymbolIDCached(Constructor),
+ SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
+ getRefContainer(ASTNode.Parent, Opts),
+ isSpelled(FileLoc, *ND)});
}
}
}
>From 5877555de0939dc4810a9d4d4e7d84ed4a9a9a0c Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Sun, 30 Nov 2025 18:15:06 +0100
Subject: [PATCH 10/22] clangd: Added finding constructors through forwards for
AST
---
clang-tools-extra/clangd/XRefs.cpp | 53 ++++++++++++++++++-
.../clangd/unittests/XRefsTests.cpp | 26 +++++++++
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index ef45acf501612..5ce55ec474428 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -916,6 +916,47 @@ std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
namespace {
+class ForwardingToConstructorVisitor
+ : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
+public:
+ ForwardingToConstructorVisitor(
+ llvm::DenseSet<const CXXConstructorDecl *> &TargetConstructors)
+ : Targets(TargetConstructors) {}
+
+ bool VisitCXXConstructExpr(CXXConstructExpr *E) {
+ if (auto *Callee = E->getConstructor()) {
+ if (Targets.contains(Callee)) {
+ ConstructorFound = true;
+ }
+ }
+ // It is enough to find 1 constructor
+ return !ConstructorFound;
+ }
+
+ // Output of this visitor
+ bool ConstructorFound = false;
+
+private:
+ llvm::DenseSet<const CXXConstructorDecl *> &Targets;
+};
+
+bool forwardsToConstructor(
+ const Decl *D,
+ llvm::DenseSet<const CXXConstructorDecl *> &TargetConstructors) {
+ if (!TargetConstructors.empty()) {
+ if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+ FD && FD->isTemplateInstantiation()) {
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunction(PT)) {
+ ForwardingToConstructorVisitor Visitor{TargetConstructors};
+ Visitor.TraverseStmt(FD->getBody());
+ return Visitor.ConstructorFound;
+ }
+ }
+ }
+ return false;
+}
+
/// Collects references to symbols within the main file.
class ReferenceFinder : public index::IndexDataConsumer {
public:
@@ -933,8 +974,12 @@ class ReferenceFinder : public index::IndexDataConsumer {
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() && {
@@ -960,8 +1005,10 @@ class ReferenceFinder : public index::IndexDataConsumer {
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, TargetConstructors)) {
return true;
+ }
const SourceManager &SM = AST.getSourceManager();
if (!isInsideMainFile(Loc, SM))
return true;
@@ -1002,6 +1049,8 @@ class ReferenceFinder : public index::IndexDataConsumer {
std::vector<Reference> References;
const 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/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index d3b6faa5fa25f..836c0a0134885 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -2714,6 +2714,32 @@ TEST(FindReferences, NoQueryForLocalSymbols) {
}
}
+TEST(FindReferences, ForwardingInAST) {
+ 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, ForwardingInIndex) {
Annotations Header(R"cpp(
namespace std {
>From 949c63114e0848a36c0defd37d46337a8337a15b Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 3 Dec 2025 02:16:47 +0100
Subject: [PATCH 11/22] clangd: Added caching for forwading functions
constructor calls
---
clang-tools-extra/clangd/AST.h | 31 +++++++-
clang-tools-extra/clangd/ParsedAST.h | 4 +
clang-tools-extra/clangd/XRefs.cpp | 78 ++++++++-----------
.../clangd/index/SymbolCollector.cpp | 64 ++++++++-------
.../clangd/index/SymbolCollector.h | 4 +
5 files changed, 107 insertions(+), 74 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index af45ae2d9022d..ffb1f269ee7ba 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -19,6 +19,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/MacroInfo.h"
@@ -254,9 +255,37 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
bool isExpandedFromParameterPack(const ParmVarDecl *D);
/// Heuristic that checks if FT is forwarding a parameter pack to another
-/// function. (e.g. `make_unique`).
+/// function (e.g. `make_unique`).
bool isLikelyForwardingFunction(FunctionTemplateDecl *FT);
+
+class ForwardingToConstructorVisitor
+ : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
+public:
+ ForwardingToConstructorVisitor() {}
+
+ ForwardingToConstructorVisitor(
+ llvm::DenseSet<const CXXConstructorDecl *> *TargetConstructors)
+ : Targets(TargetConstructors) {}
+
+ bool VisitCXXNewExpr(CXXNewExpr *E) {
+ if (auto *CE = E->getConstructExpr()) {
+ if (auto *Callee = CE->getConstructor()) {
+ if (Targets == nullptr || Targets->contains(Callee)) {
+ Constructors.push_back(Callee);
+ }
+ }
+ }
+ return true;
+ }
+
+ // Output of this visitor
+ std::vector<CXXConstructorDecl *> Constructors{};
+
+private:
+ llvm::DenseSet<const CXXConstructorDecl *> *Targets = nullptr;
+};
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h
index 82fac96360488..bc9f61cb935a9 100644
--- a/clang-tools-extra/clangd/ParsedAST.h
+++ b/clang-tools-extra/clangd/ParsedAST.h
@@ -123,6 +123,10 @@ class ParsedAST {
return Resolver.get();
}
+ /// Cache for constructors called through forwarding, e.g. make_unique
+ llvm::DenseMap<const FunctionDecl *, std::vector<CXXConstructorDecl *>>
+ ForwardingToConstructorCache;
+
private:
ParsedAST(PathRef TUPath, llvm::StringRef Version,
std::shared_ptr<const PreambleData> Preamble,
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 5ce55ec474428..728218203998a 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -916,47 +916,6 @@ std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
namespace {
-class ForwardingToConstructorVisitor
- : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
-public:
- ForwardingToConstructorVisitor(
- llvm::DenseSet<const CXXConstructorDecl *> &TargetConstructors)
- : Targets(TargetConstructors) {}
-
- bool VisitCXXConstructExpr(CXXConstructExpr *E) {
- if (auto *Callee = E->getConstructor()) {
- if (Targets.contains(Callee)) {
- ConstructorFound = true;
- }
- }
- // It is enough to find 1 constructor
- return !ConstructorFound;
- }
-
- // Output of this visitor
- bool ConstructorFound = false;
-
-private:
- llvm::DenseSet<const CXXConstructorDecl *> &Targets;
-};
-
-bool forwardsToConstructor(
- const Decl *D,
- llvm::DenseSet<const CXXConstructorDecl *> &TargetConstructors) {
- if (!TargetConstructors.empty()) {
- if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
- FD && FD->isTemplateInstantiation()) {
- if (auto *PT = FD->getPrimaryTemplate();
- PT && isLikelyForwardingFunction(PT)) {
- ForwardingToConstructorVisitor Visitor{TargetConstructors};
- Visitor.TraverseStmt(FD->getBody());
- return Visitor.ConstructorFound;
- }
- }
- }
- return false;
-}
-
/// Collects references to symbols within the main file.
class ReferenceFinder : public index::IndexDataConsumer {
public:
@@ -970,7 +929,7 @@ class ReferenceFinder : public index::IndexDataConsumer {
}
};
- ReferenceFinder(const ParsedAST &AST,
+ ReferenceFinder(ParsedAST &AST,
const llvm::ArrayRef<const NamedDecl *> Targets,
bool PerToken)
: PerToken(PerToken), AST(AST) {
@@ -1000,13 +959,44 @@ 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;
+ }
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT == nullptr || !isLikelyForwardingFunction(PT)) {
+ return false;
+ }
+ if (auto Entry = AST.ForwardingToConstructorCache.find(FD);
+ Entry != AST.ForwardingToConstructorCache.end()) {
+ for (auto *Constructor : Entry->getSecond()) {
+ if (TargetConstructors.contains(Constructor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ ForwardingToConstructorVisitor Visitor{&TargetConstructors};
+ Visitor.TraverseStmt(FD->getBody());
+ auto Iter = AST.ForwardingToConstructorCache.try_emplace(
+ FD, std::move(Visitor.Constructors));
+ if (Iter.second) {
+ return !Iter.first->getSecond().empty();
+ }
+ 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()) &&
- !forwardsToConstructor(ASTNode.OrigD, TargetConstructors)) {
+ !forwardsToConstructor(ASTNode.OrigD)) {
return true;
}
const SourceManager &SM = AST.getSourceManager();
@@ -1047,7 +1037,7 @@ 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;
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index bc5ebbef31fbd..517932f153798 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -25,11 +25,11 @@
#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"
#include "clang/AST/Expr.h"
-#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
@@ -52,6 +52,7 @@
#include <optional>
#include <string>
#include <utility>
+#include <vector>
namespace clang {
namespace clangd {
@@ -577,21 +578,29 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
-class ForwardingToConstructorVisitor
- : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
-public:
- ForwardingToConstructorVisitor() {}
-
- bool VisitCXXConstructExpr(CXXConstructExpr *E) {
- if (auto *Callee = E->getConstructor()) {
- Constructors.push_back(Callee);
- }
- return true;
+std::vector<CXXConstructorDecl *>
+SymbolCollector::findIndirectConstructors(const Decl *D) {
+ auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+ if (FD == nullptr || !FD->isTemplateInstantiation()) {
+ return {};
}
-
- // Output of this visitor
- std::vector<CXXConstructorDecl *> Constructors{};
-};
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT == nullptr || !isLikelyForwardingFunction(PT)) {
+ return {};
+ }
+ if (auto Entry = ForwardingToConstructorCache.find(FD);
+ Entry != ForwardingToConstructorCache.end()) {
+ return Entry->getSecond();
+ }
+ ForwardingToConstructorVisitor Visitor{};
+ Visitor.TraverseStmt(FD->getBody());
+ auto Iter = ForwardingToConstructorCache.try_emplace(
+ FD, std::move(Visitor.Constructors));
+ if (Iter.second) {
+ return Iter.first->getSecond();
+ }
+ return {};
+}
// Always return true to continue indexing.
bool SymbolCollector::handleDeclOccurrence(
@@ -683,22 +692,19 @@ 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`
- if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(ASTNode.OrigD);
- FD && FD->isTemplateInstantiation()) {
- if (auto *PT = FD->getPrimaryTemplate();
- PT && isLikelyForwardingFunction(PT)) {
- ForwardingToConstructorVisitor Visitor{};
- Visitor.TraverseStmt(FD->getBody());
- for (auto *Constructor : Visitor.Constructors) {
- addRef(getSymbolIDCached(Constructor),
- SymbolRef{FileLoc, FID, Roles, index::getSymbolInfo(ND).Kind,
- getRefContainer(ASTNode.Parent, Opts),
- isSpelled(FileLoc, *ND)});
- }
+ for (auto *Constructor : findIndirectConstructors(ASTNode.OrigD)) {
+ if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts, IsMainFileOnly)) {
+ continue;
+ }
+ if (auto ConstructorID = getSymbolIDCached(Constructor)) {
+ addRef(ConstructorID,
+ SymbolRef{FileLoc, FID, Roles,
+ index::getSymbolInfo(Constructor).Kind, Container,
+ isSpelled(FileLoc, *Constructor)});
}
}
}
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index e9eb27fd0f664..54e8ada0249f3 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -159,6 +159,8 @@ class SymbolCollector : public index::IndexDataConsumer {
void finish() override;
private:
+ std::vector<CXXConstructorDecl *> findIndirectConstructors(const Decl *D);
+
const Symbol *addDeclaration(const NamedDecl &, SymbolID,
bool IsMainFileSymbol);
void addDefinition(const NamedDecl &, const Symbol &DeclSymbol,
@@ -230,6 +232,8 @@ 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 *, std::vector<CXXConstructorDecl *>>
+ ForwardingToConstructorCache;
};
} // namespace clangd
>From 4811317ddb6459f273008f1c6224fc5b13799087 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 3 Dec 2025 02:21:50 +0100
Subject: [PATCH 12/22] clangd: formatting
---
clang-tools-extra/clangd/AST.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index ffb1f269ee7ba..938689104c50a 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -258,7 +258,6 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D);
/// function (e.g. `make_unique`).
bool isLikelyForwardingFunction(FunctionTemplateDecl *FT);
-
class ForwardingToConstructorVisitor
: public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
public:
>From 0aabfa235d2e48b847ab6e464838833d617a3191 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 3 Dec 2025 08:58:03 +0100
Subject: [PATCH 13/22] clangd: fixed cache not recording every constructor in
AST case
---
clang-tools-extra/clangd/AST.h | 11 +----------
clang-tools-extra/clangd/XRefs.cpp | 23 ++++++++++++++---------
2 files changed, 15 insertions(+), 19 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 938689104c50a..69dec87bed0ee 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -263,16 +263,10 @@ class ForwardingToConstructorVisitor
public:
ForwardingToConstructorVisitor() {}
- ForwardingToConstructorVisitor(
- llvm::DenseSet<const CXXConstructorDecl *> *TargetConstructors)
- : Targets(TargetConstructors) {}
-
bool VisitCXXNewExpr(CXXNewExpr *E) {
if (auto *CE = E->getConstructExpr()) {
if (auto *Callee = CE->getConstructor()) {
- if (Targets == nullptr || Targets->contains(Callee)) {
- Constructors.push_back(Callee);
- }
+ Constructors.push_back(Callee);
}
}
return true;
@@ -280,9 +274,6 @@ class ForwardingToConstructorVisitor
// Output of this visitor
std::vector<CXXConstructorDecl *> Constructors{};
-
-private:
- llvm::DenseSet<const CXXConstructorDecl *> *Targets = nullptr;
};
} // namespace clangd
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 728218203998a..dd79c59b7b0bf 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -971,21 +971,26 @@ class ReferenceFinder : public index::IndexDataConsumer {
PT == nullptr || !isLikelyForwardingFunction(PT)) {
return false;
}
+ std::vector<CXXConstructorDecl *> *Constructors = nullptr;
if (auto Entry = AST.ForwardingToConstructorCache.find(FD);
Entry != AST.ForwardingToConstructorCache.end()) {
- for (auto *Constructor : Entry->getSecond()) {
+ Constructors = &Entry->getSecond();
+ }
+ if (Constructors == nullptr) {
+ ForwardingToConstructorVisitor Visitor{};
+ Visitor.TraverseStmt(FD->getBody());
+ auto Iter = AST.ForwardingToConstructorCache.try_emplace(
+ FD, std::move(Visitor.Constructors));
+ if (Iter.second) {
+ Constructors = &Iter.first->getSecond();
+ }
+ }
+ if (Constructors != nullptr) {
+ for (auto *Constructor : *Constructors) {
if (TargetConstructors.contains(Constructor)) {
return true;
}
}
- return false;
- }
- ForwardingToConstructorVisitor Visitor{&TargetConstructors};
- Visitor.TraverseStmt(FD->getBody());
- auto Iter = AST.ForwardingToConstructorCache.try_emplace(
- FD, std::move(Visitor.Constructors));
- if (Iter.second) {
- return !Iter.first->getSecond().empty();
}
return false;
}
>From e2d2d4ebbe601e7b0c6bee8dbaadde1854037100 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Sun, 7 Dec 2025 12:54:22 +0100
Subject: [PATCH 14/22] clangd: Find constructors through multiple forwards
---
clang-tools-extra/clangd/AST.cpp | 22 ++++++++++++++
clang-tools-extra/clangd/AST.h | 11 ++-----
.../clangd/unittests/XRefsTests.cpp | 29 +++++++++++++++++++
3 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 4b73bdba8aaa9..0b2c6c2e721d3 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1061,5 +1061,27 @@ bool isLikelyForwardingFunction(FunctionTemplateDecl *FT) {
return false;
}
+bool ForwardingToConstructorVisitor::VisitCallExpr(CallExpr *E) {
+ if (auto *FD = E->getDirectCallee()) {
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunction(PT)) {
+ ForwardingToConstructorVisitor Visitor{};
+ Visitor.TraverseStmt(FD->getBody());
+ std::move(Visitor.Constructors.begin(), Visitor.Constructors.end(),
+ std::back_inserter(Constructors));
+ }
+ }
+ return true;
+}
+
+bool ForwardingToConstructorVisitor::VisitCXXNewExpr(CXXNewExpr *E) {
+ if (auto *CE = E->getConstructExpr()) {
+ if (auto *Callee = CE->getConstructor()) {
+ Constructors.push_back(Callee);
+ }
+ }
+ return true;
+}
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 69dec87bed0ee..3a31dff458421 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -263,14 +263,9 @@ class ForwardingToConstructorVisitor
public:
ForwardingToConstructorVisitor() {}
- bool VisitCXXNewExpr(CXXNewExpr *E) {
- if (auto *CE = E->getConstructExpr()) {
- if (auto *Callee = CE->getConstructor()) {
- Constructors.push_back(Callee);
- }
- }
- return true;
- }
+ bool VisitCallExpr(CallExpr *E);
+
+ bool VisitCXXNewExpr(CXXNewExpr *E);
// Output of this visitor
std::vector<CXXConstructorDecl *> Constructors{};
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 836c0a0134885..cdcdaa9e3a18b 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -2740,6 +2740,35 @@ TEST(FindReferences, ForwardingInAST) {
rangeIs(Main.range("Caller"))));
}
+TEST(FindReferences, ForwardingInASTTwice) {
+ 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)...);
+ }
+ }
+
+ struct Test {
+ $Constructor[[T^est]](){}
+ };
+
+ int main() {
+ auto a = std::$Caller[[make_unique2]]<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, ForwardingInIndex) {
Annotations Header(R"cpp(
namespace std {
>From 7843cf1ee374d4ad34b382bd6ee764a7c2c0ca30 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Mon, 15 Dec 2025 02:21:03 -0500
Subject: [PATCH 15/22] Add a background index test
---
.../clangd/unittests/BackgroundIndexTests.cpp | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
index ada14c9939318..6c9a3d3ee5b04 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"
@@ -233,6 +234,55 @@ 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, MainFileRefs) {
MockFS FS;
FS.Files[testPath("root/A.h")] = R"cpp(
>From dd3327897173e49958617e3d49548ed44f71c63b Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Tue, 16 Dec 2025 10:49:09 +0100
Subject: [PATCH 16/22] clangd: delayed indexing to make function bodies
available
---
clang-tools-extra/clangd/AST.cpp | 3 +--
clang-tools-extra/clangd/XRefs.cpp | 25 +++++++------------
.../clangd/index/IndexAction.cpp | 3 +++
.../clangd/index/SymbolCollector.cpp | 19 ++++++--------
clang/include/clang/Index/IndexingOptions.h | 3 +++
clang/lib/Index/IndexingAction.cpp | 22 ++++++++++++++--
6 files changed, 43 insertions(+), 32 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 0b2c6c2e721d3..736fa1dfd04af 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1076,9 +1076,8 @@ bool ForwardingToConstructorVisitor::VisitCallExpr(CallExpr *E) {
bool ForwardingToConstructorVisitor::VisitCXXNewExpr(CXXNewExpr *E) {
if (auto *CE = E->getConstructExpr()) {
- if (auto *Callee = CE->getConstructor()) {
+ if (auto *Callee = CE->getConstructor())
Constructors.push_back(Callee);
- }
}
return true;
}
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index dd79c59b7b0bf..d20651060b5c9 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -935,9 +935,8 @@ class ReferenceFinder : public index::IndexDataConsumer {
: PerToken(PerToken), AST(AST) {
for (const NamedDecl *ND : Targets) {
TargetDecls.insert(ND->getCanonicalDecl());
- if (auto *Constructor = llvm::dyn_cast<clang::CXXConstructorDecl>(ND)) {
+ if (auto *Constructor = llvm::dyn_cast<clang::CXXConstructorDecl>(ND))
TargetConstructors.insert(Constructor);
- }
}
}
@@ -960,36 +959,31 @@ class ReferenceFinder : public index::IndexDataConsumer {
}
bool forwardsToConstructor(const Decl *D) {
- if (TargetConstructors.empty()) {
+ if (TargetConstructors.empty())
return false;
- }
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
- if (FD == nullptr || !FD->isTemplateInstantiation()) {
+ if (FD == nullptr || !FD->isTemplateInstantiation())
return false;
- }
if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunction(PT)) {
+ PT == nullptr || !isLikelyForwardingFunction(PT))
return false;
- }
+
std::vector<CXXConstructorDecl *> *Constructors = nullptr;
if (auto Entry = AST.ForwardingToConstructorCache.find(FD);
- Entry != AST.ForwardingToConstructorCache.end()) {
+ Entry != AST.ForwardingToConstructorCache.end())
Constructors = &Entry->getSecond();
- }
if (Constructors == nullptr) {
ForwardingToConstructorVisitor Visitor{};
Visitor.TraverseStmt(FD->getBody());
auto Iter = AST.ForwardingToConstructorCache.try_emplace(
FD, std::move(Visitor.Constructors));
- if (Iter.second) {
+ if (Iter.second)
Constructors = &Iter.first->getSecond();
- }
}
if (Constructors != nullptr) {
for (auto *Constructor : *Constructors) {
- if (TargetConstructors.contains(Constructor)) {
+ if (TargetConstructors.contains(Constructor))
return true;
- }
}
}
return false;
@@ -1001,9 +995,8 @@ class ReferenceFinder : public index::IndexDataConsumer {
SourceLocation Loc,
index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
if (!TargetDecls.contains(D->getCanonicalDecl()) &&
- !forwardsToConstructor(ASTNode.OrigD)) {
+ !forwardsToConstructor(ASTNode.OrigD))
return true;
- }
const SourceManager &SM = AST.getSourceManager();
if (!isInsideMainFile(Loc, SM))
return true;
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index ed56c2a9d2e81..793516793b7d9 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -220,6 +220,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 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 517932f153798..c1b6b059fb821 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -581,24 +581,21 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
std::vector<CXXConstructorDecl *>
SymbolCollector::findIndirectConstructors(const Decl *D) {
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
- if (FD == nullptr || !FD->isTemplateInstantiation()) {
+ if (FD == nullptr || !FD->isTemplateInstantiation())
return {};
- }
if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunction(PT)) {
+ PT == nullptr || !isLikelyForwardingFunction(PT))
return {};
- }
if (auto Entry = ForwardingToConstructorCache.find(FD);
- Entry != ForwardingToConstructorCache.end()) {
+ Entry != ForwardingToConstructorCache.end())
return Entry->getSecond();
- }
+
ForwardingToConstructorVisitor Visitor{};
Visitor.TraverseStmt(FD->getBody());
auto Iter = ForwardingToConstructorCache.try_emplace(
FD, std::move(Visitor.Constructors));
- if (Iter.second) {
+ if (Iter.second)
return Iter.first->getSecond();
- }
return {};
}
@@ -697,15 +694,13 @@ bool SymbolCollector::handleDeclOccurrence(
Container, isSpelled(FileLoc, *ND)});
// Also collect indirect constructor calls like `make_unique`
for (auto *Constructor : findIndirectConstructors(ASTNode.OrigD)) {
- if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts, IsMainFileOnly)) {
+ if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts, IsMainFileOnly))
continue;
- }
- if (auto ConstructorID = getSymbolIDCached(Constructor)) {
+ if (auto ConstructorID = getSymbolIDCached(Constructor))
addRef(ConstructorID,
SymbolRef{FileLoc, FID, Roles,
index::getSymbolInfo(Constructor).Kind, Container,
isSpelled(FileLoc, *Constructor)});
- }
}
}
}
diff --git a/clang/include/clang/Index/IndexingOptions.h b/clang/include/clang/Index/IndexingOptions.h
index 97847dd7d5d88..f184ac2ebf3f4 100644
--- a/clang/include/clang/Index/IndexingOptions.h
+++ b/clang/include/clang/Index/IndexingOptions.h
@@ -36,6 +36,9 @@ 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.
+ 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..f12e90a965d8e 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"
@@ -15,6 +16,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Serialization/ASTReader.h"
#include <memory>
+#include <vector>
using namespace clang;
using namespace clang::index;
@@ -101,6 +103,8 @@ class IndexASTConsumer final : public ASTConsumer {
std::shared_ptr<IndexingContext> IndexCtx;
std::shared_ptr<Preprocessor> PP;
std::function<bool(const Decl *)> ShouldSkipFunctionBody;
+ bool DeferIndexingToEndOfTranslationUnit;
+ std::vector<DeclGroupRef> TopLevelDecls;
public:
IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer,
@@ -110,7 +114,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,6 +130,10 @@ class IndexASTConsumer final : public ASTConsumer {
}
bool HandleTopLevelDecl(DeclGroupRef DG) override {
+ if (DeferIndexingToEndOfTranslationUnit) {
+ TopLevelDecls.emplace_back(std::move(DG));
+ return true;
+ }
return IndexCtx->indexDeclGroupRef(DG);
}
@@ -132,10 +142,18 @@ class IndexASTConsumer final : public ASTConsumer {
}
void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
- IndexCtx->indexDeclGroupRef(DG);
+ if (DeferIndexingToEndOfTranslationUnit)
+ TopLevelDecls.emplace_back(std::move(DG));
+ else
+ IndexCtx->indexDeclGroupRef(DG);
}
void HandleTranslationUnit(ASTContext &Ctx) override {
+ if (DeferIndexingToEndOfTranslationUnit) {
+ for (auto &DG : TopLevelDecls) {
+ IndexCtx->indexDeclGroupRef(DG);
+ }
+ }
DataConsumer->finish();
}
>From 948040cb4ca69be22e0bf1b441010497efe9d40b Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Wed, 17 Dec 2025 18:59:21 +0100
Subject: [PATCH 17/22] Review comments and include cleanup
---
clang-tools-extra/clangd/AST.cpp | 73 ++++++++++++++-----
clang-tools-extra/clangd/AST.h | 22 ++----
clang-tools-extra/clangd/ParsedAST.h | 2 +-
clang-tools-extra/clangd/XRefs.cpp | 24 +++---
.../clangd/index/IndexAction.cpp | 5 +-
.../clangd/index/SymbolCollector.cpp | 29 ++++----
.../clangd/index/SymbolCollector.h | 7 +-
.../clangd/unittests/BackgroundIndexTests.cpp | 1 -
.../clangd/unittests/XRefsTests.cpp | 16 ++--
clang/include/clang/Index/IndexingOptions.h | 5 +-
clang/lib/Index/IndexingAction.cpp | 22 ++----
11 files changed, 116 insertions(+), 90 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 736fa1dfd04af..d770358212e23 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"
@@ -32,7 +31,6 @@
#include "clang/Sema/HeuristicResolver.h"
#include "llvm/ADT/ArrayRef.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"
@@ -1061,25 +1059,66 @@ bool isLikelyForwardingFunction(FunctionTemplateDecl *FT) {
return false;
}
-bool ForwardingToConstructorVisitor::VisitCallExpr(CallExpr *E) {
- if (auto *FD = E->getDirectCallee()) {
- if (auto *PT = FD->getPrimaryTemplate();
- PT && isLikelyForwardingFunction(PT)) {
- ForwardingToConstructorVisitor Visitor{};
- Visitor.TraverseStmt(FD->getBody());
- std::move(Visitor.Constructors.begin(), Visitor.Constructors.end(),
- std::back_inserter(Constructors));
+class ForwardingToConstructorVisitor
+ : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
+public:
+ struct SeenFunctions {
+ unsigned int DepthLeft;
+ SeenFunctions *Prev;
+ const FunctionDecl *Current;
+ };
+
+ ForwardingToConstructorVisitor(SeenFunctions SF,
+ SmallVector<CXXConstructorDecl *, 1> &Output)
+ : SF(std::move(SF)), Constructors(Output) {}
+
+ bool seenFunction(const FunctionDecl *FD) {
+ if (SF.Current == FD)
+ return true;
+ if (SF.Prev == nullptr)
+ return false;
+ return seenFunction(SF.Prev->Current);
+ }
+
+ bool VisitCallExpr(CallExpr *E) {
+ if (SF.DepthLeft == 0)
+ return true;
+ if (auto *FD = E->getDirectCallee()) {
+ // Check if we already visited this function to prevent endless recursion
+ if (seenFunction(FD))
+ return true;
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunction(PT)) {
+ SeenFunctions Next{SF.DepthLeft - 1, &SF, FD};
+ ForwardingToConstructorVisitor Visitor{std::move(Next), Constructors};
+ Visitor.TraverseStmt(FD->getBody());
+ std::move(Visitor.Constructors.begin(), Visitor.Constructors.end(),
+ std::back_inserter(Constructors));
+ }
}
+ return true;
}
- return true;
-}
-bool ForwardingToConstructorVisitor::VisitCXXNewExpr(CXXNewExpr *E) {
- if (auto *CE = E->getConstructExpr()) {
- if (auto *Callee = CE->getConstructor())
- Constructors.push_back(Callee);
+ bool VisitCXXNewExpr(CXXNewExpr *E) {
+ if (auto *CE = E->getConstructExpr())
+ if (auto *Callee = CE->getConstructor())
+ Constructors.push_back(Callee);
+ return true;
}
- return true;
+
+ // List of function stack
+ SeenFunctions SF;
+ // Output of this visitor
+ SmallVector<CXXConstructorDecl *, 1> &Constructors;
+};
+
+SmallVector<CXXConstructorDecl *, 1>
+searchConstructorsInForwardingFunction(const FunctionDecl *FD) {
+ SmallVector<CXXConstructorDecl *, 1> Result;
+ ForwardingToConstructorVisitor::SeenFunctions SF{10, nullptr, FD};
+ ForwardingToConstructorVisitor Visitor{std::move(SF), Result};
+ Visitor.TraverseStmt(FD->getBody());
+ return Result;
}
} // namespace clangd
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 3a31dff458421..152abdffcbb84 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -18,8 +18,6 @@
#include "index/SymbolID.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
-#include "clang/AST/NestedNameSpecifier.h"
-#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/MacroInfo.h"
@@ -254,22 +252,14 @@ 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 forwarding a parameter pack to another
-/// function (e.g. `make_unique`).
+/// Heuristic that checks if FT is likely to be forwarding a parameter pack to
+/// another function (e.g. `make_unique`).
bool isLikelyForwardingFunction(FunctionTemplateDecl *FT);
-class ForwardingToConstructorVisitor
- : public RecursiveASTVisitor<ForwardingToConstructorVisitor> {
-public:
- ForwardingToConstructorVisitor() {}
-
- bool VisitCallExpr(CallExpr *E);
-
- bool VisitCXXNewExpr(CXXNewExpr *E);
-
- // Output of this visitor
- std::vector<CXXConstructorDecl *> Constructors{};
-};
+/// Only call if FD is a likely forwarding function. Returns
+/// constructors that might be forwraded to
+SmallVector<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 bc9f61cb935a9..715909e9654cc 100644
--- a/clang-tools-extra/clangd/ParsedAST.h
+++ b/clang-tools-extra/clangd/ParsedAST.h
@@ -124,7 +124,7 @@ class ParsedAST {
}
/// Cache for constructors called through forwarding, e.g. make_unique
- llvm::DenseMap<const FunctionDecl *, std::vector<CXXConstructorDecl *>>
+ llvm::DenseMap<const FunctionDecl *, SmallVector<CXXConstructorDecl *, 1>>
ForwardingToConstructorCache;
private:
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index d20651060b5c9..05a97a6af4ec1 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"
@@ -964,28 +961,27 @@ class ReferenceFinder : public index::IndexDataConsumer {
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
if (FD == nullptr || !FD->isTemplateInstantiation())
return false;
- if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunction(PT))
- return false;
- std::vector<CXXConstructorDecl *> *Constructors = nullptr;
+ SmallVector<CXXConstructorDecl *, 1> *Constructors = nullptr;
if (auto Entry = AST.ForwardingToConstructorCache.find(FD);
Entry != AST.ForwardingToConstructorCache.end())
Constructors = &Entry->getSecond();
if (Constructors == nullptr) {
- ForwardingToConstructorVisitor Visitor{};
- Visitor.TraverseStmt(FD->getBody());
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT == nullptr || !isLikelyForwardingFunction(PT))
+ return false;
+
+ SmallVector<CXXConstructorDecl *, 1> FoundConstructors =
+ searchConstructorsInForwardingFunction(FD);
auto Iter = AST.ForwardingToConstructorCache.try_emplace(
- FD, std::move(Visitor.Constructors));
+ FD, std::move(FoundConstructors));
if (Iter.second)
Constructors = &Iter.first->getSecond();
}
- if (Constructors != nullptr) {
- for (auto *Constructor : *Constructors) {
+ if (Constructors != nullptr)
+ for (auto *Constructor : *Constructors)
if (TargetConstructors.contains(Constructor))
return true;
- }
- }
return false;
}
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index 793516793b7d9..4f70e9fb88c8f 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>
@@ -220,8 +219,8 @@ 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 function bodies become available, this is so
- // we can find constructor calls through `make_unique`.
+ // 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)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index c1b6b059fb821..b55b340fa3c39 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -45,14 +45,12 @@
#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>
#include <optional>
#include <string>
#include <utility>
-#include <vector>
namespace clang {
namespace clangd {
@@ -578,22 +576,22 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
-std::vector<CXXConstructorDecl *>
+SmallVector<CXXConstructorDecl *, 1>
SymbolCollector::findIndirectConstructors(const Decl *D) {
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
if (FD == nullptr || !FD->isTemplateInstantiation())
return {};
- if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunction(PT))
- return {};
if (auto Entry = ForwardingToConstructorCache.find(FD);
Entry != ForwardingToConstructorCache.end())
return Entry->getSecond();
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT == nullptr || !isLikelyForwardingFunction(PT))
+ return {};
- ForwardingToConstructorVisitor Visitor{};
- Visitor.TraverseStmt(FD->getBody());
+ SmallVector<CXXConstructorDecl *, 1> FoundConstructors =
+ searchConstructorsInForwardingFunction(FD);
auto Iter = ForwardingToConstructorCache.try_emplace(
- FD, std::move(Visitor.Constructors));
+ FD, std::move(FoundConstructors));
if (Iter.second)
return Iter.first->getSecond();
return {};
@@ -662,10 +660,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))
@@ -694,7 +694,8 @@ bool SymbolCollector::handleDeclOccurrence(
Container, isSpelled(FileLoc, *ND)});
// Also collect indirect constructor calls like `make_unique`
for (auto *Constructor : findIndirectConstructors(ASTNode.OrigD)) {
- if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts, IsMainFileOnly))
+ if (!shouldCollectSymbol(*Constructor, *ASTCtx, Opts,
+ CheckIsMainFileOnly(Constructor)))
continue;
if (auto ConstructorID = getSymbolIDCached(Constructor))
addRef(ConstructorID,
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index 54e8ada0249f3..66a9b41eaa875 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -159,7 +159,10 @@ class SymbolCollector : public index::IndexDataConsumer {
void finish() override;
private:
- std::vector<CXXConstructorDecl *> findIndirectConstructors(const Decl *D);
+ // 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<CXXConstructorDecl *, 1> findIndirectConstructors(const Decl *D);
const Symbol *addDeclaration(const NamedDecl &, SymbolID,
bool IsMainFileSymbol);
@@ -232,7 +235,7 @@ 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 *, std::vector<CXXConstructorDecl *>>
+ llvm::DenseMap<const FunctionDecl *, SmallVector<CXXConstructorDecl *, 1>>
ForwardingToConstructorCache;
};
diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
index 6c9a3d3ee5b04..afd56428fca62 100644
--- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
+++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
@@ -15,7 +15,6 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <deque>
-#include <thread>
using ::testing::_;
using ::testing::AllOf;
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index cdcdaa9e3a18b..792252cba2b7e 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -312,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;
}
@@ -2740,7 +2741,7 @@ TEST(FindReferences, ForwardingInAST) {
rangeIs(Main.range("Caller"))));
}
-TEST(FindReferences, ForwardingInASTTwice) {
+TEST(FindReferences, ForwardingInASTChained) {
Annotations Main(R"cpp(
namespace std {
template <class T> T &&forward(T &t);
@@ -2788,15 +2789,18 @@ TEST(FindReferences, ForwardingInIndex) {
}
)cpp");
TestWorkspace TW;
- TW.addMainFile("header.hpp", Header.code());
+ 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(rangeIs(Header.range()), rangeIs(Main.range())));
+ 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(GetNonLocalDeclRefs, All) {
diff --git a/clang/include/clang/Index/IndexingOptions.h b/clang/include/clang/Index/IndexingOptions.h
index f184ac2ebf3f4..c670797e9fa60 100644
--- a/clang/include/clang/Index/IndexingOptions.h
+++ b/clang/include/clang/Index/IndexingOptions.h
@@ -37,7 +37,10 @@ struct IndexingOptions {
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.
+ // 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.
diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp
index f12e90a965d8e..8118ceda9cd23 100644
--- a/clang/lib/Index/IndexingAction.cpp
+++ b/clang/lib/Index/IndexingAction.cpp
@@ -16,7 +16,6 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Serialization/ASTReader.h"
#include <memory>
-#include <vector>
using namespace clang;
using namespace clang::index;
@@ -104,7 +103,6 @@ class IndexASTConsumer final : public ASTConsumer {
std::shared_ptr<Preprocessor> PP;
std::function<bool(const Decl *)> ShouldSkipFunctionBody;
bool DeferIndexingToEndOfTranslationUnit;
- std::vector<DeclGroupRef> TopLevelDecls;
public:
IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer,
@@ -130,11 +128,9 @@ class IndexASTConsumer final : public ASTConsumer {
}
bool HandleTopLevelDecl(DeclGroupRef DG) override {
- if (DeferIndexingToEndOfTranslationUnit) {
- TopLevelDecls.emplace_back(std::move(DG));
- return true;
- }
- return IndexCtx->indexDeclGroupRef(DG);
+ if (!DeferIndexingToEndOfTranslationUnit)
+ return IndexCtx->indexDeclGroupRef(DG);
+ return true;
}
void HandleInterestingDecl(DeclGroupRef DG) override {
@@ -142,18 +138,14 @@ class IndexASTConsumer final : public ASTConsumer {
}
void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
- if (DeferIndexingToEndOfTranslationUnit)
- TopLevelDecls.emplace_back(std::move(DG));
- else
+ if (!DeferIndexingToEndOfTranslationUnit)
IndexCtx->indexDeclGroupRef(DG);
}
void HandleTranslationUnit(ASTContext &Ctx) override {
- if (DeferIndexingToEndOfTranslationUnit) {
- for (auto &DG : TopLevelDecls) {
- IndexCtx->indexDeclGroupRef(DG);
- }
- }
+ if (DeferIndexingToEndOfTranslationUnit)
+ for (auto *DG : Ctx.getTranslationUnitDecl()->decls())
+ IndexCtx->indexTopLevelDecl(DG);
DataConsumer->finish();
}
>From f27df7d02b194977af6ec810ad47956f48d17883 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Thu, 18 Dec 2025 14:38:57 +0100
Subject: [PATCH 18/22] Fixed recursive check for seen functions
---
clang-tools-extra/clangd/AST.cpp | 18 +++++++++---------
.../clangd/unittests/XRefsTests.cpp | 5 ++++-
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index d770358212e23..f647f00cad56a 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1066,26 +1066,26 @@ class ForwardingToConstructorVisitor
unsigned int DepthLeft;
SeenFunctions *Prev;
const FunctionDecl *Current;
+
+ bool seenFunction(const FunctionDecl *FD) {
+ if (Current == FD)
+ return true;
+ if (Prev == nullptr)
+ return false;
+ return Prev->seenFunction(FD);
+ }
};
ForwardingToConstructorVisitor(SeenFunctions SF,
SmallVector<CXXConstructorDecl *, 1> &Output)
: SF(std::move(SF)), Constructors(Output) {}
- bool seenFunction(const FunctionDecl *FD) {
- if (SF.Current == FD)
- return true;
- if (SF.Prev == nullptr)
- return false;
- return seenFunction(SF.Prev->Current);
- }
-
bool VisitCallExpr(CallExpr *E) {
if (SF.DepthLeft == 0)
return true;
if (auto *FD = E->getDirectCallee()) {
// Check if we already visited this function to prevent endless recursion
- if (seenFunction(FD))
+ if (SF.seenFunction(FD))
return true;
if (auto *PT = FD->getPrimaryTemplate();
PT && isLikelyForwardingFunction(PT)) {
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 792252cba2b7e..dbceba606bddd 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -2751,6 +2751,9 @@ TEST(FindReferences, ForwardingInASTChained) {
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 {
@@ -2758,7 +2761,7 @@ TEST(FindReferences, ForwardingInASTChained) {
};
int main() {
- auto a = std::$Caller[[make_unique2]]<Test>();
+ auto a = std::$Caller[[make_unique3]]<Test>();
}
)cpp");
TestTU TU;
>From 46c13b4a8349f00f8bca98cf6cd8d4e51847e46d Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Fri, 19 Dec 2025 10:23:11 +0100
Subject: [PATCH 19/22] Removed legacy code creating memory corruption
---
clang-tools-extra/clangd/AST.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index f647f00cad56a..f77f74e49b453 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1092,8 +1092,6 @@ class ForwardingToConstructorVisitor
SeenFunctions Next{SF.DepthLeft - 1, &SF, FD};
ForwardingToConstructorVisitor Visitor{std::move(Next), Constructors};
Visitor.TraverseStmt(FD->getBody());
- std::move(Visitor.Constructors.begin(), Visitor.Constructors.end(),
- std::back_inserter(Constructors));
}
}
return true;
>From ff9033d92eaef323f089f79b5dd623ba9fb79036 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Tue, 23 Dec 2025 00:31:57 +0100
Subject: [PATCH 20/22] Fix skipping function bodies necessary for finding
constructors
---
clang-tools-extra/clangd/AST.cpp | 2 +-
clang-tools-extra/clangd/AST.h | 2 +-
.../clangd/index/IndexAction.cpp | 4 +++
.../clangd/index/SymbolCollector.cpp | 25 ++++++++++++++++++-
.../clangd/index/SymbolCollector.h | 8 ++++++
5 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index f77f74e49b453..347b685e67074 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1038,7 +1038,7 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D) {
return getUnderlyingPackType(D) != nullptr;
}
-bool isLikelyForwardingFunction(FunctionTemplateDecl *FT) {
+bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT) {
const auto *FD = FT->getTemplatedDecl();
const auto NumParams = FD->getNumParams();
// Check whether its last parameter is a parameter pack...
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 152abdffcbb84..ca8dfc0702cef 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -254,7 +254,7 @@ 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(FunctionTemplateDecl *FT);
+bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT);
/// Only call if FD is a likely forwarding function. Returns
/// constructors that might be forwraded to
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index 4f70e9fb88c8f..6afe9dcee8149 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -145,6 +145,10 @@ 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 (Collector->potentiallyForwardInBody(D))
+ return true;
auto &SM = D->getASTContext().getSourceManager();
auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
if (!FID.isValid())
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index b55b340fa3c39..0ed55ef935789 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -576,6 +576,29 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
+bool SymbolCollector::isLikelyForwardingFunctionCached(
+ const FunctionTemplateDecl *FT) {
+ if (LikelyForwardingFunctionCached.contains(FT))
+ return true;
+ if (isLikelyForwardingFunction(FT)) {
+ LikelyForwardingFunctionCached.insert(FT);
+ return true;
+ }
+ return false;
+}
+
+bool SymbolCollector::potentiallyForwardInBody(const Decl *D) {
+ if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+ FD && FD->isTemplateInstantiation())
+ if (auto *PT = FD->getPrimaryTemplate();
+ PT && isLikelyForwardingFunctionCached(PT))
+ return true;
+ if (auto *FT = llvm::dyn_cast<clang::FunctionTemplateDecl>(D);
+ FT && isLikelyForwardingFunctionCached(FT))
+ return true;
+ return false;
+}
+
SmallVector<CXXConstructorDecl *, 1>
SymbolCollector::findIndirectConstructors(const Decl *D) {
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
@@ -585,7 +608,7 @@ SymbolCollector::findIndirectConstructors(const Decl *D) {
Entry != ForwardingToConstructorCache.end())
return Entry->getSecond();
if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunction(PT))
+ PT == nullptr || !isLikelyForwardingFunctionCached(PT))
return {};
SmallVector<CXXConstructorDecl *, 1> FoundConstructors =
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index 66a9b41eaa875..1269fca1a2dd1 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -19,6 +19,7 @@
#include "index/SymbolOrigin.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -151,6 +152,10 @@ class SymbolCollector : public index::IndexDataConsumer {
RefSlab takeRefs() { return std::move(Refs).build(); }
RelationSlab takeRelations() { return std::move(Relations).build(); }
+ /// Checks if \p D is either a likely forwarding function template or an
+ /// instation of it.
+ bool potentiallyForwardInBody(const Decl *D);
+
/// Returns true if we are interested in references and declarations from \p
/// FID. If this function return false, bodies of functions inside those files
/// will be skipped to decrease indexing time.
@@ -159,6 +164,8 @@ class SymbolCollector : public index::IndexDataConsumer {
void finish() override;
private:
+ bool isLikelyForwardingFunctionCached(const FunctionTemplateDecl *FT);
+
// 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.
@@ -235,6 +242,7 @@ class SymbolCollector : public index::IndexDataConsumer {
std::unique_ptr<HeaderFileURICache> HeaderFileURIs;
llvm::DenseMap<const Decl *, SymbolID> DeclToIDCache;
llvm::DenseMap<const MacroInfo *, SymbolID> MacroToIDCache;
+ llvm::DenseSet<const FunctionTemplateDecl *> LikelyForwardingFunctionCached;
llvm::DenseMap<const FunctionDecl *, SmallVector<CXXConstructorDecl *, 1>>
ForwardingToConstructorCache;
};
>From d638a2354709e7222e0186b29090276aa23be053 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Tue, 23 Dec 2025 10:50:00 +0100
Subject: [PATCH 21/22] Review comments and cleanup
---
clang-tools-extra/clangd/AST.cpp | 2 +-
clang-tools-extra/clangd/AST.h | 2 +-
.../clangd/index/IndexAction.cpp | 3 +-
.../clangd/index/SymbolCollector.cpp | 31 ++-----------------
.../clangd/index/SymbolCollector.h | 8 -----
5 files changed, 7 insertions(+), 39 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 347b685e67074..18acfe93a6a7b 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1104,7 +1104,7 @@ class ForwardingToConstructorVisitor
return true;
}
- // List of function stack
+ // Stack of seen functions
SeenFunctions SF;
// Output of this visitor
SmallVector<CXXConstructorDecl *, 1> &Constructors;
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index ca8dfc0702cef..e2f600240795e 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -257,7 +257,7 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D);
bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT);
/// Only call if FD is a likely forwarding function. Returns
-/// constructors that might be forwraded to
+/// constructors that might be forwarded to.
SmallVector<CXXConstructorDecl *, 1>
searchConstructorsInForwardingFunction(const FunctionDecl *FD);
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index 6afe9dcee8149..09943400f6d86 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -147,7 +147,8 @@ class IndexAction : public ASTFrontendAction {
return false;
// If D is a likely forwarding function we need the body to index indirect
// constructor calls (e.g. `make_unique`)
- if (Collector->potentiallyForwardInBody(D))
+ 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()));
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 0ed55ef935789..1fc42c4b4c50f 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -576,29 +576,6 @@ SymbolCollector::getRefContainer(const Decl *Enclosing,
return Enclosing;
}
-bool SymbolCollector::isLikelyForwardingFunctionCached(
- const FunctionTemplateDecl *FT) {
- if (LikelyForwardingFunctionCached.contains(FT))
- return true;
- if (isLikelyForwardingFunction(FT)) {
- LikelyForwardingFunctionCached.insert(FT);
- return true;
- }
- return false;
-}
-
-bool SymbolCollector::potentiallyForwardInBody(const Decl *D) {
- if (auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
- FD && FD->isTemplateInstantiation())
- if (auto *PT = FD->getPrimaryTemplate();
- PT && isLikelyForwardingFunctionCached(PT))
- return true;
- if (auto *FT = llvm::dyn_cast<clang::FunctionTemplateDecl>(D);
- FT && isLikelyForwardingFunctionCached(FT))
- return true;
- return false;
-}
-
SmallVector<CXXConstructorDecl *, 1>
SymbolCollector::findIndirectConstructors(const Decl *D) {
auto *FD = llvm::dyn_cast<clang::FunctionDecl>(D);
@@ -608,16 +585,14 @@ SymbolCollector::findIndirectConstructors(const Decl *D) {
Entry != ForwardingToConstructorCache.end())
return Entry->getSecond();
if (auto *PT = FD->getPrimaryTemplate();
- PT == nullptr || !isLikelyForwardingFunctionCached(PT))
+ PT == nullptr || !isLikelyForwardingFunction(PT))
return {};
SmallVector<CXXConstructorDecl *, 1> FoundConstructors =
searchConstructorsInForwardingFunction(FD);
auto Iter = ForwardingToConstructorCache.try_emplace(
FD, std::move(FoundConstructors));
- if (Iter.second)
- return Iter.first->getSecond();
- return {};
+ return Iter.first->getSecond();
}
// Always return true to continue indexing.
@@ -724,7 +699,7 @@ bool SymbolCollector::handleDeclOccurrence(
addRef(ConstructorID,
SymbolRef{FileLoc, FID, Roles,
index::getSymbolInfo(Constructor).Kind, Container,
- isSpelled(FileLoc, *Constructor)});
+ false});
}
}
}
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index 1269fca1a2dd1..66a9b41eaa875 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -19,7 +19,6 @@
#include "index/SymbolOrigin.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -152,10 +151,6 @@ class SymbolCollector : public index::IndexDataConsumer {
RefSlab takeRefs() { return std::move(Refs).build(); }
RelationSlab takeRelations() { return std::move(Relations).build(); }
- /// Checks if \p D is either a likely forwarding function template or an
- /// instation of it.
- bool potentiallyForwardInBody(const Decl *D);
-
/// Returns true if we are interested in references and declarations from \p
/// FID. If this function return false, bodies of functions inside those files
/// will be skipped to decrease indexing time.
@@ -164,8 +159,6 @@ class SymbolCollector : public index::IndexDataConsumer {
void finish() override;
private:
- bool isLikelyForwardingFunctionCached(const FunctionTemplateDecl *FT);
-
// 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.
@@ -242,7 +235,6 @@ class SymbolCollector : public index::IndexDataConsumer {
std::unique_ptr<HeaderFileURICache> HeaderFileURIs;
llvm::DenseMap<const Decl *, SymbolID> DeclToIDCache;
llvm::DenseMap<const MacroInfo *, SymbolID> MacroToIDCache;
- llvm::DenseSet<const FunctionTemplateDecl *> LikelyForwardingFunctionCached;
llvm::DenseMap<const FunctionDecl *, SmallVector<CXXConstructorDecl *, 1>>
ForwardingToConstructorCache;
};
>From 8456c8dd493ad124048af059fa66908456a49527 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <timon.ulrich at advantest.com>
Date: Tue, 23 Dec 2025 18:45:22 +0100
Subject: [PATCH 22/22] Added test for not skipping function bodies that are
likely forwarding
---
.../clangd/unittests/BackgroundIndexTests.cpp | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
index afd56428fca62..0eb4acf0469b7 100644
--- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
+++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
@@ -282,6 +282,77 @@ TEST_F(BackgroundIndexTest, ConstructorForwarding) {
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(
More information about the cfe-commits
mailing list