https://github.com/timon-ul updated 
https://github.com/llvm/llvm-project/pull/177273

>From ff644a0ff41d1521436567b10501e112bda93759 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Sat, 17 Jan 2026 03:16:06 +0100
Subject: [PATCH 01/18] First step towards indexing template instantiations

---
 clang-tools-extra/clangd/XRefs.cpp            | 42 +++++++++++--------
 .../clangd/index/SymbolCollector.cpp          | 13 +++++-
 .../clangd/unittests/XRefsTests.cpp           | 20 +++++++++
 clang/include/clang/Index/IndexingOptions.h   |  2 +-
 clang/lib/Index/IndexDecl.cpp                 | 33 ++++++++++++++-
 clang/lib/Index/IndexTypeSourceInfo.cpp       |  9 ++++
 clang/lib/Index/IndexingContext.cpp           |  4 +-
 7 files changed, 98 insertions(+), 25 deletions(-)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index 8a24d19a7d129..a517abba7762a 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -306,25 +306,31 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
 
   RelationsRequest Req;
   Req.Predicate = Predicate;
-  Req.Subjects = std::move(IDs);
+  llvm::DenseSet<SymbolID> RecursiveSearch = std::move(IDs);
   std::vector<LocatedSymbol> Results;
-  Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
-    auto DeclLoc =
-        indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
-    if (!DeclLoc) {
-      elog("Find overrides: {0}", DeclLoc.takeError());
-      return;
-    }
-    Results.emplace_back();
-    Results.back().Name = Object.Name.str();
-    Results.back().PreferredDeclaration = *DeclLoc;
-    auto DefLoc = indexToLSPLocation(Object.Definition, MainFilePath);
-    if (!DefLoc) {
-      elog("Failed to convert location: {0}", DefLoc.takeError());
-      return;
-    }
-    Results.back().Definition = *DefLoc;
-  });
+
+  while (!RecursiveSearch.empty()) {
+    Req.Subjects = std::move(RecursiveSearch);
+    RecursiveSearch = {};
+    Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
+      auto DeclLoc =
+          indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
+      if (!DeclLoc) {
+        elog("Find overrides: {0}", DeclLoc.takeError());
+        return;
+      }
+      Results.emplace_back();
+      Results.back().Name = Object.Name.str();
+      Results.back().PreferredDeclaration = *DeclLoc;
+      auto DefLoc = indexToLSPLocation(Object.Definition, MainFilePath);
+      if (!DefLoc) {
+        elog("Failed to convert location: {0}", DefLoc.takeError());
+        return;
+      }
+      Results.back().Definition = *DefLoc;
+      RecursiveSearch.insert(Object.ID);
+    });
+  }
   return Results;
 }
 
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index bd974e4c18818..8d905ad1fea10 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -603,6 +603,12 @@ bool SymbolCollector::handleDeclOccurrence(
   assert(ASTCtx && PP && HeaderFileURIs);
   assert(CompletionAllocator && CompletionTUInfo);
   assert(ASTNode.OrigD);
+  const NamedDecl *NOD = dyn_cast<NamedDecl>(ASTNode.OrigD);
+  std::string NameOD = "";
+  if (NOD){
+    NameOD = printName(*ASTCtx, *NOD);
+    NameOD += printTemplateSpecializationArgs(*NOD);
+  }
   // Indexing API puts canonical decl into D, which might not have a valid
   // source location for implicit/built-in decls. Fallback to original decl in
   // such cases.
@@ -896,9 +902,12 @@ void SymbolCollector::processRelations(
     //       in the index and find nothing, but that's a situation they
     //       probably need to handle for other reasons anyways.
     // We currently do (B) because it's simpler.
-    if (*RKind == RelationKind::BaseOf)
+    if (*RKind == RelationKind::BaseOf) {
+      std::string SubjectName = printName(*ASTCtx, ND) + 
printTemplateSpecializationArgs(ND);
+      const auto *Sym = Symbols.find(ObjectID);
+      std::string ObjectName = (Sym->Scope + Sym->Name + 
Sym->TemplateSpecializationArgs).str();
       this->Relations.insert({ID, *RKind, ObjectID});
-    else if (*RKind == RelationKind::OverriddenBy)
+    } else if (*RKind == RelationKind::OverriddenBy)
       this->Relations.insert({ObjectID, *RKind, ID});
   }
 }
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 4106c6cf7b2d0..5cefbeb78071a 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -1958,6 +1958,26 @@ TEST(FindImplementations, InheritanceObjC) {
                                        Code.range("protocolDef"))));
 }
 
+TEST(FindImplementations, InheritanceRecursive) {
+  Annotations Main(R"cpp(
+    template <typename... T>
+    struct Inherit : T... {};
+
+    struct Al$Alpha^pha {};
+
+    struct $Impl1[[impl]]: Inherit<Alpha> {};
+  )cpp");
+
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+
+  EXPECT_THAT(
+      findImplementations(AST, Main.point("Alpha"), Index.get()),
+      ElementsAre(sym("impl", Main.range("Impl1"), Main.range("Impl1"))));
+}
+
 TEST(FindImplementations, CaptureDefinition) {
   llvm::StringRef Test = R"cpp(
     struct Base {
diff --git a/clang/include/clang/Index/IndexingOptions.h 
b/clang/include/clang/Index/IndexingOptions.h
index c670797e9fa60..1094ec46dc9af 100644
--- a/clang/include/clang/Index/IndexingOptions.h
+++ b/clang/include/clang/Index/IndexingOptions.h
@@ -27,7 +27,7 @@ struct IndexingOptions {
   SystemSymbolFilterKind SystemSymbolFilter =
       SystemSymbolFilterKind::DeclarationsOnly;
   bool IndexFunctionLocals = false;
-  bool IndexImplicitInstantiation = false;
+  bool IndexImplicitInstantiation = true;
   bool IndexMacros = true;
   // Whether to index macro definitions in the Preprocessor when preprocessor
   // callback is not available (e.g. after parsing has finished). Note that
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index df875e0b40079..51b400f915bd3 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/DeclVisitor.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexSymbol.h"
+#include "llvm/Support/Casting.h"
 
 using namespace clang;
 using namespace index;
@@ -734,7 +735,37 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
       indexTemplateParameters(Params, Parent);
     }
 
-    return Visit(Parent);
+    bool shouldContinue = Visit(Parent);
+    if (!shouldContinue)
+      return false;
+
+    // TODO: cleanup maybe, so far copy paste from
+    // RecursiveASTVisitor::TraverseTemplateInstantiation, we have technically
+    // `shouldIndexImplicitInstantiation()` available here, but the logic is
+    // different and I am confused.
+    if (const auto *CTD = llvm::dyn_cast<ClassTemplateDecl>(D))
+      for (auto *SD : CTD->specializations())
+        for (auto *RD : SD->redecls()) {
+          assert(!cast<CXXRecordDecl>(RD)->isInjectedClassName());
+          switch (cast<ClassTemplateSpecializationDecl>(RD)
+                      ->getSpecializationKind()) {
+          // Visit the implicit instantiations with the requested pattern.
+          case TSK_Undeclared:
+          case TSK_ImplicitInstantiation:
+            Visit(RD);
+            break;
+
+          // We don't need to do anything on an explicit instantiation
+          // or explicit specialization because there will be an explicit
+          // node for it elsewhere.
+          case TSK_ExplicitInstantiationDeclaration:
+          case TSK_ExplicitInstantiationDefinition:
+          case TSK_ExplicitSpecialization:
+            break;
+          }
+        }
+
+    return true;
   }
 
   bool VisitConceptDecl(const ConceptDecl *D) {
diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index c9ad36b5406c5..be3d75e84c351 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -10,6 +10,7 @@
 #include "clang/AST/ASTConcept.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TypeBase.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Sema/HeuristicResolver.h"
 #include "llvm/ADT/ScopeExit.h"
@@ -193,6 +194,14 @@ class TypeIndexer : public 
RecursiveASTVisitor<TypeIndexer> {
     return true;
   }
 
+  // bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
+  //                                           bool TraverseQualifier) {
+  //   auto Type = TL.getAs<TemplateSpecializationTypeLoc>();
+  //   if (!Type.isNull())
+  //     TraverseTemplateSpecializationTypeLoc(Type, TraverseQualifier);
+  //   return true;
+  // }
+
   bool 
VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc 
TL) {
     auto *T = TL.getTypePtr();
     if (!T)
diff --git a/clang/lib/Index/IndexingContext.cpp 
b/clang/lib/Index/IndexingContext.cpp
index bdd6c5acf1d34..c6843cf46cf8e 100644
--- a/clang/lib/Index/IndexingContext.cpp
+++ b/clang/lib/Index/IndexingContext.cpp
@@ -402,9 +402,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, 
SourceLocation Loc,
   if (!OrigD)
     OrigD = D;
 
-  if (isTemplateImplicitInstantiation(D)) {
-    if (!IsRef)
-      return true;
+  if (isTemplateImplicitInstantiation(D) && IsRef) {
     D = adjustTemplateImplicitInstantiation(D);
     if (!D)
       return true;

>From 511ba41d9153e13247fd5fb9eb61ec330dfb4e21 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Sat, 17 Jan 2026 03:20:22 +0100
Subject: [PATCH 02/18] New methods for visiting SubstTemplateType

---
 clang/lib/Index/IndexTypeSourceInfo.cpp | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index be3d75e84c351..e16d8ac791898 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -194,13 +194,21 @@ class TypeIndexer : public 
RecursiveASTVisitor<TypeIndexer> {
     return true;
   }
 
-  // bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
-  //                                           bool TraverseQualifier) {
-  //   auto Type = TL.getAs<TemplateSpecializationTypeLoc>();
-  //   if (!Type.isNull())
-  //     TraverseTemplateSpecializationTypeLoc(Type, TraverseQualifier);
-  //   return true;
-  // }
+  bool VisitSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL) {
+    auto QT = TL.getTypePtr()->getReplacementType();
+    auto *T = QT->getAsNonAliasTemplateSpecializationType();
+    if (!T)
+      return true;
+    HandleTemplateSpecializationTypeLoc(
+        T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
+        T->isTypeAlias());
+    return true;
+  }
+
+  bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
+                                            bool TraverseQualifier) {
+    return true;
+  }
 
   bool 
VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc 
TL) {
     auto *T = TL.getTypePtr();

>From b5ffe8fb17140df17999a61693064bec25248ec2 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Wed, 21 Jan 2026 23:20:57 +0100
Subject: [PATCH 03/18] Implemented template inheritance handling

---
 .../clangd/index/SymbolCollector.cpp          | 22 ++++++------
 .../clangd/index/SymbolCollector.h            |  2 +-
 .../clangd/unittests/XRefsTests.cpp           | 36 ++++++++++++++-----
 clang/include/clang/Index/IndexingOptions.h   |  2 +-
 clang/lib/Index/IndexDecl.cpp                 | 33 +++++++----------
 clang/lib/Index/IndexTypeSourceInfo.cpp       | 19 +++++-----
 6 files changed, 61 insertions(+), 53 deletions(-)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 8d905ad1fea10..f8ff9bf277ceb 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -603,12 +603,7 @@ bool SymbolCollector::handleDeclOccurrence(
   assert(ASTCtx && PP && HeaderFileURIs);
   assert(CompletionAllocator && CompletionTUInfo);
   assert(ASTNode.OrigD);
-  const NamedDecl *NOD = dyn_cast<NamedDecl>(ASTNode.OrigD);
-  std::string NameOD = "";
-  if (NOD){
-    NameOD = printName(*ASTCtx, *NOD);
-    NameOD += printTemplateSpecializationArgs(*NOD);
-  }
+
   // Indexing API puts canonical decl into D, which might not have a valid
   // source location for implicit/built-in decls. Fallback to original decl in
   // such cases.
@@ -679,7 +674,7 @@ bool SymbolCollector::handleDeclOccurrence(
   // refs, because the indexing code only populates relations for specific
   // occurrences. For example, RelationBaseOf is only populated for the
   // occurrence inside the base-specifier.
-  processRelations(*ND, ID, Relations);
+  processRelations(ID, *ASTNode.OrigD, Relations);
 
   bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
   // Unlike other fields, e.g. Symbols (which use spelling locations), we use
@@ -881,7 +876,7 @@ bool SymbolCollector::handleMacroOccurrence(const 
IdentifierInfo *Name,
 }
 
 void SymbolCollector::processRelations(
-    const NamedDecl &ND, const SymbolID &ID,
+    const SymbolID &ID, const Decl &OrigD,
     ArrayRef<index::SymbolRelation> Relations) {
   for (const auto &R : Relations) {
     auto RKind = indexableRelation(R);
@@ -903,10 +898,15 @@ void SymbolCollector::processRelations(
     //       probably need to handle for other reasons anyways.
     // We currently do (B) because it's simpler.
     if (*RKind == RelationKind::BaseOf) {
-      std::string SubjectName = printName(*ASTCtx, ND) + 
printTemplateSpecializationArgs(ND);
-      const auto *Sym = Symbols.find(ObjectID);
-      std::string ObjectName = (Sym->Scope + Sym->Name + 
Sym->TemplateSpecializationArgs).str();
       this->Relations.insert({ID, *RKind, ObjectID});
+      // If the Subject is a template, we also want a relation to the
+      // template instantiation (OrigD) to record inheritance chains.
+      if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(&OrigD);
+          CTSD && !CTSD->isExplicitSpecialization()) {
+        auto OrigID = getSymbolIDCached(&OrigD);
+        if (OrigID)
+          this->Relations.insert({OrigID, *RKind, ObjectID});
+      }
     } else if (*RKind == RelationKind::OverriddenBy)
       this->Relations.insert({ObjectID, *RKind, ID});
   }
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h 
b/clang-tools-extra/clangd/index/SymbolCollector.h
index 4d51d747639b1..54a12ba122240 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -169,7 +169,7 @@ class SymbolCollector : public index::IndexDataConsumer {
                                bool IsMainFileSymbol);
   void addDefinition(const NamedDecl &, const Symbol &DeclSymbol,
                      bool SkipDocCheck);
-  void processRelations(const NamedDecl &ND, const SymbolID &ID,
+  void processRelations(const SymbolID &ID, const Decl &OrigD,
                         ArrayRef<index::SymbolRelation> Relations);
 
   std::optional<SymbolLocation> getTokenLocation(SourceLocation TokLoc);
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 5cefbeb78071a..9a382f4a8c257 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -1878,8 +1878,8 @@ TEST(FindImplementations, Inheritance) {
       virtual void B$2^ar();
       void Concrete();  // No implementations for concrete methods.
     };
-    struct Child2 : Child1 {
-      void $3[[Foo]]() override;
+    struct $0[[Child2]] : Child1 {
+      void $1[[$3[[Foo]]]]() override;
       void $2[[Bar]]() override;
     };
     void FromReference() {
@@ -1958,14 +1958,27 @@ TEST(FindImplementations, InheritanceObjC) {
                                        Code.range("protocolDef"))));
 }
 
-TEST(FindImplementations, InheritanceRecursive) {
+TEST(FindImplementations, InheritanceTemplate) {
   Annotations Main(R"cpp(
+    class Fi$First^rst {};
+
+    class Sec$Second^ond {};
+
+    class Th$Third^ird {};
+
     template <typename... T>
-    struct Inherit : T... {};
+    struct $Third[[Inherit]] : T... {};
+
+    template struct $First[[Inherit]]<First>;
+
+    template<>
+    struct $Second[[Inherit]]<Second> : Second {};
 
-    struct Al$Alpha^pha {};
+    class $First[[Battler]] : Inherit<First> {};
 
-    struct $Impl1[[impl]]: Inherit<Alpha> {};
+    class $Second[[Beatrice]] : Inherit<Second> {};
+
+    class $Third[[Maria]] : Inherit<Third> {};
   )cpp");
 
   TestTU TU;
@@ -1973,9 +1986,14 @@ TEST(FindImplementations, InheritanceRecursive) {
   auto AST = TU.build();
   auto Index = TU.index();
 
-  EXPECT_THAT(
-      findImplementations(AST, Main.point("Alpha"), Index.get()),
-      ElementsAre(sym("impl", Main.range("Impl1"), Main.range("Impl1"))));
+  EXPECT_THAT(findImplementations(AST, Main.point("First"), Index.get()),
+              UnorderedPointwise(declRange(), Main.ranges("First")));
+
+  EXPECT_THAT(findImplementations(AST, Main.point("Second"), Index.get()),
+              UnorderedPointwise(declRange(), Main.ranges("Second")));
+
+  EXPECT_THAT(findImplementations(AST, Main.point("Third"), Index.get()),
+              UnorderedPointwise(declRange(), Main.ranges("Third")));
 }
 
 TEST(FindImplementations, CaptureDefinition) {
diff --git a/clang/include/clang/Index/IndexingOptions.h 
b/clang/include/clang/Index/IndexingOptions.h
index 1094ec46dc9af..c670797e9fa60 100644
--- a/clang/include/clang/Index/IndexingOptions.h
+++ b/clang/include/clang/Index/IndexingOptions.h
@@ -27,7 +27,7 @@ struct IndexingOptions {
   SystemSymbolFilterKind SystemSymbolFilter =
       SystemSymbolFilterKind::DeclarationsOnly;
   bool IndexFunctionLocals = false;
-  bool IndexImplicitInstantiation = true;
+  bool IndexImplicitInstantiation = false;
   bool IndexMacros = true;
   // Whether to index macro definitions in the Preprocessor when preprocessor
   // callback is not available (e.g. after parsing has finished). Note that
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index 51b400f915bd3..f0fe1d0af7791 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -739,30 +739,21 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
     if (!shouldContinue)
       return false;
 
-    // TODO: cleanup maybe, so far copy paste from
-    // RecursiveASTVisitor::TraverseTemplateInstantiation, we have technically
-    // `shouldIndexImplicitInstantiation()` available here, but the logic is
-    // different and I am confused.
+    // Only check instantiation if D is canonical to prevent infinite cycling
+    if (D != D->getCanonicalDecl())
+      return true;
+
     if (const auto *CTD = llvm::dyn_cast<ClassTemplateDecl>(D))
       for (auto *SD : CTD->specializations())
         for (auto *RD : SD->redecls()) {
-          assert(!cast<CXXRecordDecl>(RD)->isInjectedClassName());
-          switch (cast<ClassTemplateSpecializationDecl>(RD)
-                      ->getSpecializationKind()) {
-          // Visit the implicit instantiations with the requested pattern.
-          case TSK_Undeclared:
-          case TSK_ImplicitInstantiation:
-            Visit(RD);
-            break;
-
-          // We don't need to do anything on an explicit instantiation
-          // or explicit specialization because there will be an explicit
-          // node for it elsewhere.
-          case TSK_ExplicitInstantiationDeclaration:
-          case TSK_ExplicitInstantiationDefinition:
-          case TSK_ExplicitSpecialization:
-            break;
-          }
+          auto *CTSD = cast<ClassTemplateSpecializationDecl>(RD);
+          // For now we are only interested in instantiations with inheritance.
+          if (!CTSD->hasDefinition() || CTSD->bases().empty())
+            continue;
+          // Explicit specialization is handled elsewhere
+          if (CTSD->isExplicitSpecialization())
+            continue;
+          Visit(RD);
         }
 
     return true;
diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index e16d8ac791898..bb62b7d6f8d87 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -194,19 +194,18 @@ class TypeIndexer : public 
RecursiveASTVisitor<TypeIndexer> {
     return true;
   }
 
-  bool VisitSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL) {
-    auto QT = TL.getTypePtr()->getReplacementType();
-    auto *T = QT->getAsNonAliasTemplateSpecializationType();
+  bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
+                                            bool TraverseQualifier) {
+    const auto *T = TL.getTypePtr();
     if (!T)
       return true;
-    HandleTemplateSpecializationTypeLoc(
-        T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(),
-        T->isTypeAlias());
-    return true;
-  }
+    auto QT = T->getReplacementType();
+    if (QT.isNull())
+      return true;
+
+    IndexCtx.handleReference(QT->getAsCXXRecordDecl(), TL.getNameLoc(), Parent,
+                             ParentDC, SymbolRoleSet(), Relations);
 
-  bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
-                                            bool TraverseQualifier) {
     return true;
   }
 

>From 53c04f63a1a5583d4200a86e7d065e5ec11b4cd2 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Thu, 22 Jan 2026 00:05:17 +0100
Subject: [PATCH 04/18] Errors do not meant we shouldn't keep recursing

---
 clang-tools-extra/clangd/XRefs.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index a517abba7762a..79decd35baadd 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -313,6 +313,7 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
     Req.Subjects = std::move(RecursiveSearch);
     RecursiveSearch = {};
     Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
+      RecursiveSearch.insert(Object.ID);
       auto DeclLoc =
           indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
       if (!DeclLoc) {
@@ -328,7 +329,6 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
         return;
       }
       Results.back().Definition = *DefLoc;
-      RecursiveSearch.insert(Object.ID);
     });
   }
   return Results;

>From 6502814383b0bc95a0f69c05bf0aa126e4a9fa51 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Thu, 22 Jan 2026 00:28:59 +0100
Subject: [PATCH 05/18] Preventing refs for implicit instantiations

---
 clang-tools-extra/clangd/index/SymbolCollector.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index f8ff9bf277ceb..30f34324f5e58 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -677,6 +677,13 @@ bool SymbolCollector::handleDeclOccurrence(
   processRelations(ID, *ASTNode.OrigD, Relations);
 
   bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
+  // For now we only want the bare minimum of information for a class
+  // instantiation such that we have symbols for the `BaseOf` relation.
+  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D);
+      CTSD && CTSD->hasDefinition() && !CTSD->bases().empty() &&
+      !CTSD->isExplicitSpecialization()) {
+    CollectRef = false;
+  }
   // Unlike other fields, e.g. Symbols (which use spelling locations), we use
   // file locations for references (as it aligns the behavior of clangd's
   // AST-based xref).

>From 195fd77fca387bff1a4594437b0fb433ef243331 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Thu, 22 Jan 2026 11:05:50 +0100
Subject: [PATCH 06/18] More careful handling of casts

---
 clang/lib/Index/IndexDecl.cpp           | 5 ++---
 clang/lib/Index/IndexTypeSourceInfo.cpp | 9 +++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index f0fe1d0af7791..35f8f7e43ed7d 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -14,7 +14,6 @@
 #include "clang/AST/DeclVisitor.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexSymbol.h"
-#include "llvm/Support/Casting.h"
 
 using namespace clang;
 using namespace index;
@@ -746,9 +745,9 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
     if (const auto *CTD = llvm::dyn_cast<ClassTemplateDecl>(D))
       for (auto *SD : CTD->specializations())
         for (auto *RD : SD->redecls()) {
-          auto *CTSD = cast<ClassTemplateSpecializationDecl>(RD);
+          auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
           // For now we are only interested in instantiations with inheritance.
-          if (!CTSD->hasDefinition() || CTSD->bases().empty())
+          if (!CTSD || !CTSD->hasDefinition() || CTSD->bases().empty())
             continue;
           // Explicit specialization is handled elsewhere
           if (CTSD->isExplicitSpecialization())
diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index bb62b7d6f8d87..ee70bc05914ad 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -202,11 +202,12 @@ class TypeIndexer : public 
RecursiveASTVisitor<TypeIndexer> {
     auto QT = T->getReplacementType();
     if (QT.isNull())
       return true;
+    auto *CXXRD = QT->getAsCXXRecordDecl();
+    if (!CXXRD)
+      return true;
 
-    IndexCtx.handleReference(QT->getAsCXXRecordDecl(), TL.getNameLoc(), Parent,
-                             ParentDC, SymbolRoleSet(), Relations);
-
-    return true;
+    return IndexCtx.handleReference(CXXRD, TL.getNameLoc(), Parent, ParentDC,
+                                    SymbolRoleSet(), Relations);
   }
 
   bool 
VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc 
TL) {

>From 178fe678a5bd3b9bcb59192be43df18a397fedb2 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Mon, 26 Jan 2026 14:47:35 +0100
Subject: [PATCH 07/18] Fixed infinite recursion

---
 clang-tools-extra/clangd/XRefs.cpp            |  3 ++
 .../clangd/unittests/XRefsTests.cpp           | 36 +++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index 79decd35baadd..69f5dbc954692 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -306,6 +306,7 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
 
   RelationsRequest Req;
   Req.Predicate = Predicate;
+  llvm::DenseSet<SymbolID> SeenIDs;
   llvm::DenseSet<SymbolID> RecursiveSearch = std::move(IDs);
   std::vector<LocatedSymbol> Results;
 
@@ -313,6 +314,8 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
     Req.Subjects = std::move(RecursiveSearch);
     RecursiveSearch = {};
     Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
+      if (!SeenIDs.insert(Object.ID).second)
+        return;
       RecursiveSearch.insert(Object.ID);
       auto DeclLoc =
           indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 9a382f4a8c257..2f52169214f7b 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -1923,6 +1923,42 @@ TEST(FindImplementations, Inheritance) {
   }
 }
 
+TEST(FindImplementations, InheritanceRecursion) {
+  // Make sure inheritance is followed, but does not diverge.
+  llvm::StringRef Test = R"cpp(
+    template <int>
+    struct [[Ev^en]];
+
+    template <int>
+    struct [[Odd]];
+
+    template <>
+    struct Even<0> {
+      static const bool value = true;
+    };
+
+    template <>
+    struct Odd<0> {
+      static const bool value = false;
+    };
+
+    template <int I>
+    struct Even : Odd<I - 1> {};
+
+    template <int I>
+    struct Odd : Even<I - 1> {};
+
+    constexpr bool Answer = Even<42>::value;
+  )cpp";
+
+  Annotations Code(Test);
+  auto TU = TestTU::withCode(Code.code());
+  auto AST = TU.build();
+  auto Index = TU.index();
+  EXPECT_THAT(findImplementations(AST, Code.point(), Index.get()),
+              UnorderedPointwise(declRange(), Code.ranges()));
+}
+
 TEST(FindImplementations, InheritanceObjC) {
   llvm::StringRef Test = R"objc(
     @interface $base^Base

>From 2e6a49c51127ed55970eaa5134d36ad3a13e02f0 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Tue, 27 Jan 2026 17:11:32 +0100
Subject: [PATCH 08/18] Added defRange matcher and renamed Queue - manual merge

---
 clang-tools-extra/clangd/XRefs.cpp                | 11 +++++------
 clang-tools-extra/clangd/unittests/XRefsTests.cpp | 15 ++++++++++-----
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index 69f5dbc954692..d4398a593d48e 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -307,16 +307,15 @@ std::vector<LocatedSymbol> 
findImplementors(llvm::DenseSet<SymbolID> IDs,
   RelationsRequest Req;
   Req.Predicate = Predicate;
   llvm::DenseSet<SymbolID> SeenIDs;
-  llvm::DenseSet<SymbolID> RecursiveSearch = std::move(IDs);
+  llvm::DenseSet<SymbolID> Queue = std::move(IDs);
   std::vector<LocatedSymbol> Results;
-
-  while (!RecursiveSearch.empty()) {
-    Req.Subjects = std::move(RecursiveSearch);
-    RecursiveSearch = {};
+  while (!Queue.empty()) {
+    Req.Subjects = std::move(Queue);
+    Queue = {};
     Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
       if (!SeenIDs.insert(Object.ID).second)
         return;
-      RecursiveSearch.insert(Object.ID);
+      Queue.insert(Object.ID);
       auto DeclLoc =
           indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
       if (!DeclLoc) {
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 2f52169214f7b..1b7b46c0d71b1 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -54,6 +54,11 @@ MATCHER(declRange, "") {
   const Range &Range = ::testing::get<1>(arg);
   return Sym.PreferredDeclaration.range == Range;
 }
+MATCHER(defRange, "") {
+  const LocatedSymbol &Sym = ::testing::get<0>(arg);
+  const Range &Range = ::testing::get<1>(arg);
+  return Sym.Definition.value_or(Sym.PreferredDeclaration).range == Range;
+}
 
 // Extracts ranges from an annotated example, and constructs a matcher for a
 // highlight set. Ranges should be named $read/$write as appropriate.
@@ -1927,10 +1932,10 @@ TEST(FindImplementations, InheritanceRecursion) {
   // Make sure inheritance is followed, but does not diverge.
   llvm::StringRef Test = R"cpp(
     template <int>
-    struct [[Ev^en]];
+    struct Ev^en;
 
     template <int>
-    struct [[Odd]];
+    struct Odd;
 
     template <>
     struct Even<0> {
@@ -1943,10 +1948,10 @@ TEST(FindImplementations, InheritanceRecursion) {
     };
 
     template <int I>
-    struct Even : Odd<I - 1> {};
+    struct [[Even]] : Odd<I - 1> {};
 
     template <int I>
-    struct Odd : Even<I - 1> {};
+    struct [[Odd]] : Even<I - 1> {};
 
     constexpr bool Answer = Even<42>::value;
   )cpp";
@@ -1956,7 +1961,7 @@ TEST(FindImplementations, InheritanceRecursion) {
   auto AST = TU.build();
   auto Index = TU.index();
   EXPECT_THAT(findImplementations(AST, Code.point(), Index.get()),
-              UnorderedPointwise(declRange(), Code.ranges()));
+              UnorderedPointwise(defRange(), Code.ranges()));
 }
 
 TEST(FindImplementations, InheritanceObjC) {

>From 3f37408884aac1f4fbf643c24fe8d0bc122467f3 Mon Sep 17 00:00:00 2001
From: Timon Ulrich <[email protected]>
Date: Wed, 28 Jan 2026 18:06:45 +0100
Subject: [PATCH 09/18] Fixed InheritanceRecursion test for newly found
 instantiations

---
 clang-tools-extra/clangd/unittests/XRefsTests.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp 
b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 1b7b46c0d71b1..07434d26479c2 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -1948,12 +1948,12 @@ TEST(FindImplementations, InheritanceRecursion) {
     };
 
     template <int I>
-    struct [[Even]] : Odd<I - 1> {};
+    struct [[[[Even]]]] : Odd<I - 1> {};
 
     template <int I>
     struct [[Odd]] : Even<I - 1> {};
 
-    constexpr bool Answer = Even<42>::value;
+    constexpr bool Answer = Even<2>::value;
   )cpp";
 
   Annotations Code(Test);

>From 854c308fa718576a163c64b8beb09410057000bf Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Fri, 6 Mar 2026 14:30:20 +0100
Subject: [PATCH 10/18] Hide implicit template indexing behind indexing option

---
 clang-tools-extra/clangd/XRefs.cpp                          | 1 +
 clang-tools-extra/clangd/index/FileIndex.cpp                | 1 +
 clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp | 1 +
 clang/lib/Index/IndexDecl.cpp                               | 3 +++
 4 files changed, 6 insertions(+)

diff --git a/clang-tools-extra/clangd/XRefs.cpp 
b/clang-tools-extra/clangd/XRefs.cpp
index d4398a593d48e..ff5e35e6bf62a 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -1051,6 +1051,7 @@ findRefs(const llvm::ArrayRef<const NamedDecl *> 
TargetDecls, ParsedAST &AST,
   IndexOpts.SystemSymbolFilter =
       index::IndexingOptions::SystemSymbolFilterKind::All;
   IndexOpts.IndexFunctionLocals = true;
+  IndexOpts.IndexImplicitInstantiation = true;
   IndexOpts.IndexParametersInDeclarations = true;
   IndexOpts.IndexTemplateParameters = true;
   indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp 
b/clang-tools-extra/clangd/index/FileIndex.cpp
index 2e005bfe3537e..4234886ce8eaf 100644
--- a/clang-tools-extra/clangd/index/FileIndex.cpp
+++ b/clang-tools-extra/clangd/index/FileIndex.cpp
@@ -65,6 +65,7 @@ SlabTuple indexSymbols(ASTContext &AST, Preprocessor &PP,
       index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
   // We index function-local classes and its member functions only.
   IndexOpts.IndexFunctionLocals = true;
+  IndexOpts.IndexImplicitInstantiation = true;
   if (IsIndexMainAST) {
     // We only collect refs when indexing main AST.
     CollectorOpts.RefFilter = RefKind::All;
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 94116fca3cbb2..2906b78f69471 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -310,6 +310,7 @@ class SymbolIndexActionFactory : public 
tooling::FrontendActionFactory {
     IndexOpts.SystemSymbolFilter =
         index::IndexingOptions::SystemSymbolFilterKind::All;
     IndexOpts.IndexFunctionLocals = true;
+    IndexOpts.IndexImplicitInstantiation = true;
     std::shared_ptr<include_cleaner::PragmaIncludes> PI =
         std::make_shared<include_cleaner::PragmaIncludes>();
     COpts.PragmaIncludes = PI.get();
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index 35f8f7e43ed7d..cf8486298b993 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -738,6 +738,9 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
     if (!shouldContinue)
       return false;
 
+    if (!IndexCtx.shouldIndexImplicitInstantiation())
+      return true;
+
     // Only check instantiation if D is canonical to prevent infinite cycling
     if (D != D->getCanonicalDecl())
       return true;

>From 744d0bbcda1b49526329a955bcacff2e83e97acd Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Mon, 9 Mar 2026 19:46:32 +0100
Subject: [PATCH 11/18] Adjusted behaviour for SymbolCollectorTest.Tempalte

---
 clang-tools-extra/clangd/AST.cpp                   |  7 +++++++
 clang-tools-extra/clangd/AST.h                     |  5 +++++
 clang-tools-extra/clangd/CodeComplete.cpp          |  4 ++--
 .../clangd/unittests/SymbolCollectorTests.cpp      | 14 ++++++++++----
 4 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 3bcc89d360cdb..eaf3d9adc80d8 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -180,6 +180,13 @@ std::string getQualification(ASTContext &Context,
 
 } // namespace
 
+bool isTemplateInstantiation(const NamedDecl *D) {
+  return isTemplateSpecializationKind(D,
+                                      TSK_ExplicitInstantiationDeclaration) ||
+         isTemplateSpecializationKind(D, TSK_ExplicitInstantiationDefinition) 
||
+         isTemplateSpecializationKind(D, TSK_ImplicitInstantiation);
+}
+
 bool isImplicitTemplateInstantiation(const NamedDecl *D) {
   return isTemplateSpecializationKind(D, TSK_ImplicitInstantiation);
 }
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 2bb4943b6de0b..c08e26472c528 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -138,6 +138,11 @@ std::string printType(const QualType QT, const DeclContext 
&CurContext,
                       llvm::StringRef Placeholder = "",
                       bool FullyQualify = false);
 
+/// Indicates if \p D is a template instantiation, implicit or explicit e.g.
+///     template <class T> struct vector {};
+///     template <> struct vector<bool>; // is an explicit instantiation.
+///     vector<int> v; // 'vector<int>' is an implicit instantiation.
+bool isTemplateInstantiation(const NamedDecl *D);
 /// Indicates if \p D is a template instantiation implicitly generated by the
 /// compiler, e.g.
 ///     template <class T> struct vector {};
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index f43b5e71a1dfa..a0e3a9b5353ee 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -2368,8 +2368,8 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, 
ASTContext &ASTCtx) {
     return ND.getDeclContext()->getDeclKind() == Decl::CXXRecord;
   };
   // We only complete symbol's name, which is the same as the name of the
-  // *primary* template in case of template specializations.
-  if (isExplicitTemplateSpecialization(&ND))
+  // *primary* template in case of template specializations or instantiation.
+  if (isExplicitTemplateSpecialization(&ND) || isTemplateInstantiation(&ND))
     return false;
 
   // Category decls are not useful on their own outside the interface or
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 2906b78f69471..109585d072397 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -486,13 +486,11 @@ TEST_F(SymbolCollectorTest, FileLocal) {
 
 TEST_F(SymbolCollectorTest, Template) {
   Annotations Header(R"(
-    // Primary template and explicit specialization are indexed, instantiation
-    // is not.
     template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
     template <> struct $specdecl[[Tmpl]]<int, bool> {};
     template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
-    extern template struct Tmpl<float, bool>;
-    template struct Tmpl<double, bool>;
+    extern template struct $extinst[[Tmpl]]<float, bool>;
+    template struct $inst[[Tmpl]]<double, bool>;
   )");
   runSymbolCollector(Header.code(), /*Main=*/"");
   EXPECT_THAT(Symbols,
@@ -503,7 +501,15 @@ TEST_F(SymbolCollectorTest, Template) {
                         forCodeCompletion(false)),
                   AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
                         forCodeCompletion(false)),
+                  AllOf(qName("Tmpl"), declRange(Header.range("extinst")),
+                        forCodeCompletion(false)),
+                  AllOf(qName("Tmpl"), declRange(Header.range("inst")),
+                        forCodeCompletion(false)),
                   AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
+                        forCodeCompletion(false)),
+                  AllOf(qName("Tmpl<float, bool>::x"), 
declRange(Header.range("xdecl")),
+                        forCodeCompletion(false)),
+                  AllOf(qName("Tmpl<double, bool>::x"), 
declRange(Header.range("xdecl")),
                         forCodeCompletion(false))));
 }
 

>From 121265f34cc80725b7caadd96c26141dcb436b09 Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Mon, 9 Mar 2026 21:46:05 +0100
Subject: [PATCH 12/18] Prevent doc creation for implicit instantiations

---
 clang-tools-extra/clangd/index/SymbolCollector.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 30f34324f5e58..4c45a0479a2ba 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -1118,6 +1118,11 @@ const Symbol *SymbolCollector::addDeclaration(const 
NamedDecl &ND, SymbolID ID,
   if (ND.getAvailability() == AR_Deprecated)
     S.Flags |= Symbol::Deprecated;
 
+  if (isImplicitTemplateInstantiation(&ND)) {
+    Symbols.insert(S);
+    return Symbols.find(S.ID);
+  }
+
   // Add completion info.
   // FIXME: we may want to choose a different redecl, or combine from several.
   assert(ASTCtx && PP && "ASTContext and Preprocessor must be set.");

>From e29de3d462f7d8e2c25880589e570a473d0b4c14 Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Tue, 10 Mar 2026 18:44:43 +0100
Subject: [PATCH 13/18] Added test for libIndex new behaviour

---
 clang/unittests/Index/IndexTests.cpp | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/clang/unittests/Index/IndexTests.cpp 
b/clang/unittests/Index/IndexTests.cpp
index 6df4b577d98a0..2926f206a3207 100644
--- a/clang/unittests/Index/IndexTests.cpp
+++ b/clang/unittests/Index/IndexTests.cpp
@@ -16,6 +16,7 @@
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Index/IndexingAction.h"
+#include "clang/Index/IndexingOptions.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/StringRef.h"
@@ -259,6 +260,28 @@ TEST(IndexTest, IndexExplicitTemplateInstantiation) {
                                    DeclAt(Position(3, 12))))));
 }
 
+TEST(IndexTest, IndexImplicitTemplateInstantiation) {
+  std::string Code = R"cpp(
+    struct Seagulls {};
+    struct Cry {};
+
+    template <typename... T>
+    struct The : T... {};
+
+    struct When : The<Seagulls,Cry> {};
+  )cpp";
+  auto Index = std::make_shared<Indexer>();
+  IndexingOptions Opts;
+  Opts.IndexImplicitInstantiation = true;
+  tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);
+  EXPECT_THAT(
+      Index->Symbols,
+      AllOf(Contains(AllOf(QName("Seagulls"), WrittenAt(Position(6, 18)),
+                           DeclAt(Position(2, 12)))),
+            Contains(AllOf(QName("Cry"), WrittenAt(Position(6, 18)),
+                           DeclAt(Position(3, 12))))));
+}
+
 TEST(IndexTest, IndexTemplateInstantiationPartial) {
   std::string Code = R"cpp(
     template <typename T1, typename T2>

>From c939b39759d350ff6dbb94b70654bda896d7d9d8 Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Tue, 10 Mar 2026 19:52:30 +0100
Subject: [PATCH 14/18] Formatted test

---
 .../clangd/unittests/SymbolCollectorTests.cpp | 37 ++++++++++---------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 109585d072397..3b0a3022f6143 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -493,24 +493,25 @@ TEST_F(SymbolCollectorTest, Template) {
     template struct $inst[[Tmpl]]<double, bool>;
   )");
   runSymbolCollector(Header.code(), /*Main=*/"");
-  EXPECT_THAT(Symbols,
-              UnorderedElementsAre(
-                  AllOf(qName("Tmpl"), declRange(Header.range()),
-                        forCodeCompletion(true)),
-                  AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl"), declRange(Header.range("extinst")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl"), declRange(Header.range("inst")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl<float, bool>::x"), 
declRange(Header.range("xdecl")),
-                        forCodeCompletion(false)),
-                  AllOf(qName("Tmpl<double, bool>::x"), 
declRange(Header.range("xdecl")),
-                        forCodeCompletion(false))));
+  EXPECT_THAT(
+      Symbols,
+      UnorderedElementsAre(
+          AllOf(qName("Tmpl"), declRange(Header.range()),
+                forCodeCompletion(true)),
+          AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl"), declRange(Header.range("extinst")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl"), declRange(Header.range("inst")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl<float, bool>::x"), 
declRange(Header.range("xdecl")),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl<double, bool>::x"),
+                declRange(Header.range("xdecl")), forCodeCompletion(false))));
 }
 
 TEST_F(SymbolCollectorTest, templateArgs) {

>From e1a2e0508356978d922816d7634cc8e33186c7cb Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Wed, 11 Mar 2026 22:36:46 +0100
Subject: [PATCH 15/18] Cleanup

---
 .../clangd/index/IndexAction.cpp              |  1 +
 .../clangd/index/SymbolCollector.cpp          | 20 +++++++++----------
 clang/lib/Index/IndexDecl.cpp                 |  4 ++--
 clang/lib/Index/IndexTypeSourceInfo.cpp       |  3 +++
 4 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp 
b/clang-tools-extra/clangd/index/IndexAction.cpp
index 09943400f6d86..feb8b7a8dc6fb 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -224,6 +224,7 @@ std::unique_ptr<FrontendAction> createStaticIndexingAction(
       index::IndexingOptions::SystemSymbolFilterKind::All;
   // We index function-local classes and its member functions only.
   IndexOpts.IndexFunctionLocals = true;
+  IndexOpts.IndexImplicitInstantiation = 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;
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 4c45a0479a2ba..fcc2aae755ecf 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -1118,7 +1118,10 @@ const Symbol *SymbolCollector::addDeclaration(const 
NamedDecl &ND, SymbolID ID,
   if (ND.getAvailability() == AR_Deprecated)
     S.Flags |= Symbol::Deprecated;
 
-  if (isImplicitTemplateInstantiation(&ND)) {
+  // Computing doc comments is expensive, so if we can skip it we should do so.
+  if (isImplicitTemplateInstantiation(&ND) ||
+      (!(S.Flags & Symbol::IndexedForCodeCompletion) &&
+       !Opts.StoreAllDocumentation)) {
     Symbols.insert(S);
     return Symbols.find(S.ID);
   }
@@ -1139,21 +1142,16 @@ const Symbol *SymbolCollector::addDeclaration(const 
NamedDecl &ND, SymbolID ID,
     DocComment = getDocComment(Ctx, SymbolCompletion,
                                /*CommentsFromHeaders=*/true);
     Documentation = formatDocumentation(*CCS, DocComment);
+    if (!DocComment.empty())
+      S.Flags |= Symbol::HasDocComment;
+    S.Documentation = Documentation;
   }
-  const auto UpdateDoc = [&] {
-    if (!AlreadyHasDoc) {
-      if (!DocComment.empty())
-        S.Flags |= Symbol::HasDocComment;
-      S.Documentation = Documentation;
-    }
-  };
+
   if (!(S.Flags & Symbol::IndexedForCodeCompletion)) {
-    if (Opts.StoreAllDocumentation)
-      UpdateDoc();
     Symbols.insert(S);
     return Symbols.find(S.ID);
   }
-  UpdateDoc();
+
   std::string Signature;
   std::string SnippetSuffix;
   getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index cf8486298b993..b3a55309ad892 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -734,8 +734,8 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
       indexTemplateParameters(Params, Parent);
     }
 
-    bool shouldContinue = Visit(Parent);
-    if (!shouldContinue)
+    bool ShouldContinue = Visit(Parent);
+    if (!ShouldContinue)
       return false;
 
     if (!IndexCtx.shouldIndexImplicitInstantiation())
diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index ee70bc05914ad..1e1c7ae0330df 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -196,6 +196,9 @@ class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> 
{
 
   bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
                                             bool TraverseQualifier) {
+    // TODO: For now if we are a templated field and the substituted type is of
+    // form `A<B>`, we will only record a reference to `A`, but it is 
reasonable
+    // to also expect a reference to `B` to be recorded.
     const auto *T = TL.getTypePtr();
     if (!T)
       return true;

>From 8472ec826d0f929b707e4035e5a4175903808902 Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Fri, 27 Mar 2026 03:38:15 +0100
Subject: [PATCH 16/18] Clean up and review comments

---
 clang-tools-extra/clangd/AST.cpp              | 10 ++++++-
 clang-tools-extra/clangd/AST.h                |  3 +-
 clang-tools-extra/clangd/CodeComplete.cpp     |  2 +-
 .../clangd/index/SymbolCollector.cpp          |  7 ++---
 .../clangd/unittests/SymbolCollectorTests.cpp | 30 +++++++++++++++----
 clang/lib/Index/IndexDecl.cpp                 |  4 +--
 clang/lib/Index/IndexTypeSourceInfo.cpp       |  6 ++--
 clang/lib/Index/IndexingContext.cpp           | 15 ++++++----
 8 files changed, 54 insertions(+), 23 deletions(-)

diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index eaf3d9adc80d8..727a65b2e8867 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -180,7 +180,15 @@ std::string getQualification(ASTContext &Context,
 
 } // namespace
 
-bool isTemplateInstantiation(const NamedDecl *D) {
+bool isTemplateInstantiationScope(const NamedDecl *D) {
+  // Fields and Methods don't know about template instantiations, so we have to
+  // ask their parent.
+  if (auto *FD = dyn_cast<FieldDecl>(D))
+    if (auto *RD = FD->getParent())
+      D = RD;
+  if (auto *MD = dyn_cast<CXXMethodDecl>(D))
+    if (auto *RD = MD->getParent())
+      D = RD;
   return isTemplateSpecializationKind(D,
                                       TSK_ExplicitInstantiationDeclaration) ||
          isTemplateSpecializationKind(D, TSK_ExplicitInstantiationDefinition) 
||
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index c08e26472c528..d9636b1b4b4a1 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -142,7 +142,8 @@ std::string printType(const QualType QT, const DeclContext 
&CurContext,
 ///     template <class T> struct vector {};
 ///     template <> struct vector<bool>; // is an explicit instantiation.
 ///     vector<int> v; // 'vector<int>' is an implicit instantiation.
-bool isTemplateInstantiation(const NamedDecl *D);
+/// Also returns true for methods and fields inside of an instantiation.
+bool isTemplateInstantiationScope(const NamedDecl *D);
 /// Indicates if \p D is a template instantiation implicitly generated by the
 /// compiler, e.g.
 ///     template <class T> struct vector {};
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index a0e3a9b5353ee..56c3678c0ab74 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -2369,7 +2369,7 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, 
ASTContext &ASTCtx) {
   };
   // We only complete symbol's name, which is the same as the name of the
   // *primary* template in case of template specializations or instantiation.
-  if (isExplicitTemplateSpecialization(&ND) || isTemplateInstantiation(&ND))
+  if (isExplicitTemplateSpecialization(&ND) || 
isTemplateInstantiationScope(&ND))
     return false;
 
   // Category decls are not useful on their own outside the interface or
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp 
b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index fcc2aae755ecf..f85a304a2bdf6 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -34,6 +34,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/Token.h"
@@ -679,9 +680,7 @@ bool SymbolCollector::handleDeclOccurrence(
   bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles));
   // For now we only want the bare minimum of information for a class
   // instantiation such that we have symbols for the `BaseOf` relation.
-  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D);
-      CTSD && CTSD->hasDefinition() && !CTSD->bases().empty() &&
-      !CTSD->isExplicitSpecialization()) {
+  if (isTemplateInstantiationScope(ND)) {
     CollectRef = false;
   }
   // Unlike other fields, e.g. Symbols (which use spelling locations), we use
@@ -1119,7 +1118,7 @@ const Symbol *SymbolCollector::addDeclaration(const 
NamedDecl &ND, SymbolID ID,
     S.Flags |= Symbol::Deprecated;
 
   // Computing doc comments is expensive, so if we can skip it we should do so.
-  if (isImplicitTemplateInstantiation(&ND) ||
+  if (isTemplateInstantiationScope(&ND) ||
       (!(S.Flags & Symbol::IndexedForCodeCompletion) &&
        !Opts.StoreAllDocumentation)) {
     Symbols.insert(S);
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp 
b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index 3b0a3022f6143..90fb5a5b08aac 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -486,32 +486,50 @@ TEST_F(SymbolCollectorTest, FileLocal) {
 
 TEST_F(SymbolCollectorTest, Template) {
   Annotations Header(R"(
-    template <class T, class U> struct [[Tmpl]] {T $xdecl[[x]] = 0;};
+    template <class T, class U> struct [[Tmpl]] {
+      //doc only for primary x
+      T $xdecl[[x]] = 0;
+      //doc only for primary y
+      T $ydecl[[y]]();
+      //doc only for primary z
+      struct $zdecl[[z]] {};
+    };
     template <> struct $specdecl[[Tmpl]]<int, bool> {};
     template <class U> struct $partspecdecl[[Tmpl]]<bool, U> {};
     extern template struct $extinst[[Tmpl]]<float, bool>;
     template struct $inst[[Tmpl]]<double, bool>;
   )");
+  CollectorOpts.StoreAllDocumentation = true;
   runSymbolCollector(Header.code(), /*Main=*/"");
   EXPECT_THAT(
       Symbols,
       UnorderedElementsAre(
           AllOf(qName("Tmpl"), declRange(Header.range()),
                 forCodeCompletion(true)),
+          AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
+                doc("doc only for primary x"), forCodeCompletion(false)),
+          AllOf(qName("Tmpl::y"), declRange(Header.range("ydecl")),
+                doc("doc only for primary y"), forCodeCompletion(false)),
+          AllOf(qName("Tmpl::z"), declRange(Header.range("zdecl")),
+                doc("doc only for primary z"), forCodeCompletion(false)),
           AllOf(qName("Tmpl"), declRange(Header.range("specdecl")),
                 forCodeCompletion(false)),
           AllOf(qName("Tmpl"), declRange(Header.range("partspecdecl")),
                 forCodeCompletion(false)),
           AllOf(qName("Tmpl"), declRange(Header.range("extinst")),
                 forCodeCompletion(false)),
-          AllOf(qName("Tmpl"), declRange(Header.range("inst")),
-                forCodeCompletion(false)),
-          AllOf(qName("Tmpl::x"), declRange(Header.range("xdecl")),
-                forCodeCompletion(false)),
           AllOf(qName("Tmpl<float, bool>::x"), 
declRange(Header.range("xdecl")),
+                doc(""), forCodeCompletion(false)),
+          AllOf(qName("Tmpl<float, bool>::y"), 
declRange(Header.range("ydecl")),
+                doc(""), forCodeCompletion(false)),
+          AllOf(qName("Tmpl"), declRange(Header.range("inst")),
                 forCodeCompletion(false)),
           AllOf(qName("Tmpl<double, bool>::x"),
-                declRange(Header.range("xdecl")), forCodeCompletion(false))));
+                declRange(Header.range("xdecl")), doc(""),
+                forCodeCompletion(false)),
+          AllOf(qName("Tmpl<double, bool>::y"),
+                declRange(Header.range("ydecl")), doc(""),
+                forCodeCompletion(false))));
 }
 
 TEST_F(SymbolCollectorTest, templateArgs) {
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index b3a55309ad892..64b05960b7d01 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -752,8 +752,8 @@ class IndexingDeclVisitor : public 
ConstDeclVisitor<IndexingDeclVisitor, bool> {
           // For now we are only interested in instantiations with inheritance.
           if (!CTSD || !CTSD->hasDefinition() || CTSD->bases().empty())
             continue;
-          // Explicit specialization is handled elsewhere
-          if (CTSD->isExplicitSpecialization())
+          // Explicit instantiations and specializations are handled elsewhere
+          if (CTSD->isExplicitInstantiationOrSpecialization())
             continue;
           Visit(RD);
         }
diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp 
b/clang/lib/Index/IndexTypeSourceInfo.cpp
index 1e1c7ae0330df..ee7e38cb36bdc 100644
--- a/clang/lib/Index/IndexTypeSourceInfo.cpp
+++ b/clang/lib/Index/IndexTypeSourceInfo.cpp
@@ -196,15 +196,15 @@ class TypeIndexer : public 
RecursiveASTVisitor<TypeIndexer> {
 
   bool TraverseSubstTemplateTypeParmTypeLoc(SubstTemplateTypeParmTypeLoc TL,
                                             bool TraverseQualifier) {
-    // TODO: For now if we are a templated field and the substituted type is of
-    // form `A<B>`, we will only record a reference to `A`, but it is 
reasonable
-    // to also expect a reference to `B` to be recorded.
     const auto *T = TL.getTypePtr();
     if (!T)
       return true;
     auto QT = T->getReplacementType();
     if (QT.isNull())
       return true;
+    // TODO: For now if we are a templated field and the substituted type is of
+    // form `A<B>`, we will only record a reference to `A`, but it is 
reasonable
+    // to also expect a reference to `B` to be recorded.
     auto *CXXRD = QT->getAsCXXRecordDecl();
     if (!CXXRD)
       return true;
diff --git a/clang/lib/Index/IndexingContext.cpp 
b/clang/lib/Index/IndexingContext.cpp
index c6843cf46cf8e..fc2e3758fe0ac 100644
--- a/clang/lib/Index/IndexingContext.cpp
+++ b/clang/lib/Index/IndexingContext.cpp
@@ -402,11 +402,16 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, 
SourceLocation Loc,
   if (!OrigD)
     OrigD = D;
 
-  if (isTemplateImplicitInstantiation(D) && IsRef) {
-    D = adjustTemplateImplicitInstantiation(D);
-    if (!D)
-      return true;
-    assert(!isTemplateImplicitInstantiation(D));
+  if (isTemplateImplicitInstantiation(D)) {
+    if (IsRef) {
+      D = adjustTemplateImplicitInstantiation(D);
+      if (!D)
+        return true;
+      assert(!isTemplateImplicitInstantiation(D));
+    } else {
+      if (!shouldIndexImplicitInstantiation())
+        return true;
+    }
   }
 
   if (IsRef)

>From ef3984285d61f4072cfaca01cb9d1b9fc0d55eb5 Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Fri, 27 Mar 2026 03:41:07 +0100
Subject: [PATCH 17/18] formatting

---
 clang-tools-extra/clangd/CodeComplete.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/CodeComplete.cpp 
b/clang-tools-extra/clangd/CodeComplete.cpp
index 56c3678c0ab74..279133f178603 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -2369,7 +2369,8 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, 
ASTContext &ASTCtx) {
   };
   // We only complete symbol's name, which is the same as the name of the
   // *primary* template in case of template specializations or instantiation.
-  if (isExplicitTemplateSpecialization(&ND) || 
isTemplateInstantiationScope(&ND))
+  if (isExplicitTemplateSpecialization(&ND) ||
+      isTemplateInstantiationScope(&ND))
     return false;
 
   // Category decls are not useful on their own outside the interface or

>From 485cb64ddacef859c9e9af5801dd045e03e96ecc Mon Sep 17 00:00:00 2001
From: timon-ul <[email protected]>
Date: Fri, 27 Mar 2026 13:15:05 +0100
Subject: [PATCH 18/18] Fixed documentation error

---
 clang-tools-extra/clangd/AST.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index d9636b1b4b4a1..beba6b68da6ee 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -140,8 +140,8 @@ std::string printType(const QualType QT, const DeclContext 
&CurContext,
 
 /// Indicates if \p D is a template instantiation, implicit or explicit e.g.
 ///     template <class T> struct vector {};
-///     template <> struct vector<bool>; // is an explicit instantiation.
-///     vector<int> v; // 'vector<int>' is an implicit instantiation.
+///     template struct vector<bool>; // is an explicit instantiation.
+///     vector<int> v; // is an implicit instantiation.
 /// Also returns true for methods and fields inside of an instantiation.
 bool isTemplateInstantiationScope(const NamedDecl *D);
 /// Indicates if \p D is a template instantiation implicitly generated by the

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

Reply via email to