https://github.com/isanbard created 
https://github.com/llvm/llvm-project/pull/235116

Inside a class template, references to members of a dependent base, such
as 'this->size()' where the base is 'A<T>', aren't bound to a declaration until
instantiation, so clang_getCursorReferenced() returned a null cursor for them.

Fall back to HeuristicResolver, which clangd uses for the same purpose, to
look the name up in the primary template, and return the result if it's a single
declaration. This also resolves 'type_trait<T>::value' in a requires-clause or
concept to the primary template's static member.

'A<T>::size()' (a qualified reference to an instance member) still isn't 
resolved,
because HeuristicResolver only considers static members for a
DependentScopeDeclRefExpr.

Addresses part of #15370.

Assisted-by: Claude Opus 5.5

>From 66edef27ee3b44a294a8511dbf7525dc80a09bcc Mon Sep 17 00:00:00 2001
From: Bill Wendling <[email protected]>
Date: Fri, 23 Oct 2026 14:40:40 -0700
Subject: [PATCH] [libclang] Resolve dependent member references in
 clang_getCursorReferenced

Inside a class template, references to members of a dependent base, such
as 'this->size()' where the base is 'A<T>', aren't bound to a declaration
until instantiation, so clang_getCursorReferenced() returned a null cursor
for them.

Fall back to HeuristicResolver, which clangd uses for the same purpose, to
look the name up in the primary template, and return the result if it's a
single declaration. This also resolves 'type_trait<T>::value' in a
requires-clause or concept to the primary template's static member.

'A<T>::size()' (a qualified reference to an instance member) still isn't
resolved, because HeuristicResolver only considers static members for a
DependentScopeDeclRefExpr.

Addresses part of #15370.

Assisted-by: Claude Opus 5.5
---
 .../Index/cursor-ref-dependent-member.cpp     | 33 +++++++++++++++++++
 clang/test/Index/index-concepts.cpp           |  6 ++--
 clang/tools/libclang/CIndex.cpp               | 20 +++++++++++
 3 files changed, 56 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Index/cursor-ref-dependent-member.cpp

diff --git a/clang/test/Index/cursor-ref-dependent-member.cpp 
b/clang/test/Index/cursor-ref-dependent-member.cpp
new file mode 100644
index 00000000000000..0f2093a2491b26
--- /dev/null
+++ b/clang/test/Index/cursor-ref-dependent-member.cpp
@@ -0,0 +1,33 @@
+// Check that clang_getCursorReferenced() resolves member references through a
+// dependent base class to the member of the primary template.
+
+template <typename T>
+class A {
+public:
+  int size() const { return 1; }
+  int field;
+  void ov(int);
+  void ov(float);
+};
+
+template <typename T>
+class B : public A<T> {
+public:
+  int f1() const { return this->size(); }
+  int f2() const { return this->field; }
+  void f3() { this->ov(1); }
+  int f4() const { return A<T>::size(); }
+};
+
+// RUN: c-index-test -test-load-source local %s | FileCheck %s
+
+// CHECK: 16:27: CallExpr=:7:7 Extent=[16:27 - 16:39]
+// CHECK: 16:27: MemberRefExpr=:7:7 Extent=[16:27 - 16:37]
+// CHECK: 17:27: MemberRefExpr=:8:7 Extent=[17:27 - 17:38]
+
+// An overloaded member can't be resolved to a single declaration.
+// CHECK: 18:15: MemberRefExpr= Extent=[18:15 - 18:23]
+
+// HeuristicResolver only considers static members for a qualified dependent
+// name, so 'A<T>::size()' isn't resolved yet.
+// CHECK: 19:27: DeclRefExpr= Extent=[19:27 - 19:37]
diff --git a/clang/test/Index/index-concepts.cpp 
b/clang/test/Index/index-concepts.cpp
index 17fbd02f34fe8f..04a814ca4894d8 100644
--- a/clang/test/Index/index-concepts.cpp
+++ b/clang/test/Index/index-concepts.cpp
@@ -13,7 +13,7 @@ struct type_trait<int> {
 template <class T>
 requires (type_trait<T>::value)
 // CHECK: index-concepts.cpp:[[@LINE-1]]:10: ParenExpr= Extent=[[[@LINE-1]]:10 
- [[@LINE-1]]:32]
-// CHECK: index-concepts.cpp:[[@LINE-2]]:11: DeclRefExpr= 
Extent=[[[@LINE-2]]:11 - [[@LINE-2]]:31]
+// CHECK: index-concepts.cpp:[[@LINE-2]]:11: DeclRefExpr=:5:23 
Extent=[[[@LINE-2]]:11 - [[@LINE-2]]:31]
 // CHECK: index-concepts.cpp:[[@LINE-3]]:11: TemplateRef=type_trait:4:8 
Extent=[[[@LINE-3]]:11 - [[@LINE-3]]:21]
 // CHECK: index-concepts.cpp:[[@LINE-4]]:22: TypeRef=T:13:17 
Extent=[[[@LINE-4]]:22 - [[@LINE-4]]:23]
 void indexRequiresClause() {
@@ -22,7 +22,7 @@ void indexRequiresClause() {
 template<class T>
 requires (type_trait<T>::value)
 // CHECK: index-concepts.cpp:[[@LINE-1]]:10: ParenExpr= Extent=[[[@LINE-1]]:10 
- [[@LINE-1]]:32]
-// CHECK: index-concepts.cpp:[[@LINE-2]]:11: DeclRefExpr= 
Extent=[[[@LINE-2]]:11 - [[@LINE-2]]:31]
+// CHECK: index-concepts.cpp:[[@LINE-2]]:11: DeclRefExpr=:5:23 
Extent=[[[@LINE-2]]:11 - [[@LINE-2]]:31]
 // CHECK: index-concepts.cpp:[[@LINE-3]]:11: TemplateRef=type_trait:4:8 
Extent=[[[@LINE-3]]:11 - [[@LINE-3]]:21]
 // CHECK: index-concepts.cpp:[[@LINE-4]]:22: TypeRef=T:22:16 
Extent=[[[@LINE-4]]:22 - [[@LINE-4]]:23]
 class IndexRequiresClauseInClass {};
@@ -31,7 +31,7 @@ template <class T>
 concept Con1 = type_trait<T>::value;
 // CHECK: index-concepts.cpp:[[@LINE-1]]:9: ConceptDecl=Con1:[[@LINE-1]]:9 
(Definition) Extent=[[[@LINE-2]]:1 - [[@LINE-1]]:36]
 // CHECK: index-concepts.cpp:[[@LINE-3]]:17: 
TemplateTypeParameter=T:[[@LINE-3]]:17 (Definition) Extent=[[[@LINE-3]]:11 - 
[[@LINE-3]]:18] [access=public]
-// CHECK: index-concepts.cpp:[[@LINE-3]]:16: DeclRefExpr= 
Extent=[[[@LINE-3]]:16 - [[@LINE-3]]:36]
+// CHECK: index-concepts.cpp:[[@LINE-3]]:16: DeclRefExpr=:5:23 
Extent=[[[@LINE-3]]:16 - [[@LINE-3]]:36]
 // CHECK: index-concepts.cpp:[[@LINE-4]]:16: TemplateRef=type_trait:4:8 
Extent=[[[@LINE-4]]:16 - [[@LINE-4]]:26]
 // CHECK: index-concepts.cpp:[[@LINE-5]]:27: TypeRef=T:30:17 
Extent=[[[@LINE-5]]:27 - [[@LINE-5]]:28]
 
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 8d246d41f31f66..6f4dc06691ca7f 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -46,6 +46,7 @@
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/PreprocessingRecord.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/HeuristicResolver.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Config/llvm-config.h"
@@ -5281,6 +5282,23 @@ static const Decl *getDeclFromExpr(const Stmt *E) {
   return nullptr;
 }
 
+// Resolve a reference through a dependent name, such as 'A<T>::size()' or
+// 'this->size()' in a class template deriving from 'A<T>'. The AST doesn't 
bind
+// these to a declaration until instantiation, so use the same heuristics as
+// clangd to look the name up in the primary template. Return null unless
+// exactly one declaration is found, since the cursor can only name one.
+static const Decl *getDeclFromDependentExpr(const Expr *E, ASTContext &Ctx) {
+  HeuristicResolver Resolver(Ctx);
+  std::vector<const NamedDecl *> Decls;
+  if (const auto *CE = dyn_cast<CallExpr>(E))
+    Decls = Resolver.resolveCalleeOfCallExpr(CE);
+  else if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E))
+    Decls = Resolver.resolveMemberExpr(ME);
+  else if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E))
+    Decls = Resolver.resolveDeclRefExpr(RE);
+  return Decls.size() == 1 ? Decls.front() : nullptr;
+}
+
 static SourceLocation getLocationFromExpr(const Expr *E) {
   if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E))
     return getLocationFromExpr(CE->getSubExpr());
@@ -7173,6 +7191,8 @@ CXCursor clang_getCursorReferenced(CXCursor C) {
   if (clang_isExpression(C.kind)) {
     const Expr *E = getCursorExpr(C);
     const Decl *D = getDeclFromExpr(E);
+    if (!D && E)
+      D = getDeclFromDependentExpr(E, getCursorContext(C));
     if (D) {
       CXCursor declCursor = MakeCXCursor(D, tu);
       declCursor = getSelectorIdentifierCursor(getSelectorIdentifierIndex(C),

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

Reply via email to