[clang] [clang][CodeComplete] Use HeuristicResolver in getAsRecordDecl() (PR #130473)

Nathan Ridge via cfe-commits cfe-commits at lists.llvm.org
Sun Mar 9 18:56:35 PDT 2025


https://github.com/HighCommander4 updated https://github.com/llvm/llvm-project/pull/130473

>From 66e5ed1f1fcfe5a52d110ae228ddd51bd487ee16 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 01:36:25 -0500
Subject: [PATCH 1/6] Change the parameter type of resolveTypeToTagDecl() to
 QualType

---
 clang/lib/Sema/HeuristicResolver.cpp | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 7aecd2a73b539..3c7918165ee42 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -75,7 +75,7 @@ class HeuristicResolverImpl {
   // Helper function for HeuristicResolver::resolveDependentMember()
   // which takes a possibly-dependent type `T` and heuristically
   // resolves it to a TagDecl in which we can try name lookup.
-  TagDecl *resolveTypeToTagDecl(const Type *T);
+  TagDecl *resolveTypeToTagDecl(QualType T);
 
   // Helper function for simplifying a type.
   // `Type` is the type to simplify.
@@ -132,8 +132,10 @@ TemplateName getReferencedTemplateName(const Type *T) {
 // Helper function for HeuristicResolver::resolveDependentMember()
 // which takes a possibly-dependent type `T` and heuristically
 // resolves it to a CXXRecordDecl in which we can try name lookup.
-TagDecl *HeuristicResolverImpl::resolveTypeToTagDecl(const Type *T) {
-  assert(T);
+TagDecl *HeuristicResolverImpl::resolveTypeToTagDecl(QualType QT) {
+  const Type *T = QT.getTypePtrOrNull();
+  if (!T)
+    return nullptr;
 
   // Unwrap type sugar such as type aliases.
   T = T->getCanonicalTypeInternal().getTypePtr();
@@ -315,8 +317,7 @@ HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
   if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
     CalleeType = FnTypePtr->getPointeeType();
   if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
-    if (const auto *D =
-            resolveTypeToTagDecl(FnType->getReturnType().getTypePtr())) {
+    if (const auto *D = resolveTypeToTagDecl(FnType->getReturnType())) {
       return {D};
     }
   }
@@ -427,7 +428,7 @@ bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,
 bool HeuristicResolverImpl::findOrdinaryMemberInDependentClasses(
     const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
     DeclarationName Name) {
-  TagDecl *TD = resolveTypeToTagDecl(Specifier->getType().getTypePtr());
+  TagDecl *TD = resolveTypeToTagDecl(Specifier->getType());
   if (const auto *RD = dyn_cast_if_present<CXXRecordDecl>(TD)) {
     return findOrdinaryMember(RD, Path, Name);
   }
@@ -470,10 +471,7 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::lookupDependentName(
 std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
     QualType QT, DeclarationName Name,
     llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
-  const Type *T = QT.getTypePtrOrNull();
-  if (!T)
-    return {};
-  TagDecl *TD = resolveTypeToTagDecl(T);
+  TagDecl *TD = resolveTypeToTagDecl(QT);
   if (!TD)
     return {};
   if (auto *ED = dyn_cast<EnumDecl>(TD)) {

>From efa9503da4dcef74003f08493eb48c3d119cba0f Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 01:43:25 -0500
Subject: [PATCH 2/6] Expose resolveTypeToTagDecl() publically in
 HeuristicResolver

---
 clang/include/clang/Sema/HeuristicResolver.h | 4 ++++
 clang/lib/Sema/HeuristicResolver.cpp         | 9 ++++-----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h
index f511815b40199..c971b9a6a7b51 100644
--- a/clang/include/clang/Sema/HeuristicResolver.h
+++ b/clang/include/clang/Sema/HeuristicResolver.h
@@ -81,6 +81,10 @@ class HeuristicResolver {
   // could look up the name appearing on the RHS.
   const QualType getPointeeType(QualType T) const;
 
+  // Heuristically resolve a possibly-dependent type `T` to a TagDecl
+  // in which a member's name can be looked up.
+  TagDecl *resolveTypeToTagDecl(QualType T) const;
+
 private:
   ASTContext &Ctx;
 };
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 3c7918165ee42..11fe786fa91c6 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -47,6 +47,7 @@ class HeuristicResolverImpl {
   std::vector<const NamedDecl *>
   lookupDependentName(CXXRecordDecl *RD, DeclarationName Name,
                       llvm::function_ref<bool(const NamedDecl *ND)> Filter);
+  TagDecl *resolveTypeToTagDecl(QualType T);
 
 private:
   ASTContext &Ctx;
@@ -72,11 +73,6 @@ class HeuristicResolverImpl {
   QualType resolveExprToType(const Expr *E);
   std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
 
-  // Helper function for HeuristicResolver::resolveDependentMember()
-  // which takes a possibly-dependent type `T` and heuristically
-  // resolves it to a TagDecl in which we can try name lookup.
-  TagDecl *resolveTypeToTagDecl(QualType T);
-
   // Helper function for simplifying a type.
   // `Type` is the type to simplify.
   // `E` is the expression whose type `Type` is, if known. This sometimes
@@ -538,5 +534,8 @@ std::vector<const NamedDecl *> HeuristicResolver::lookupDependentName(
 const QualType HeuristicResolver::getPointeeType(QualType T) const {
   return HeuristicResolverImpl(Ctx).getPointeeType(T);
 }
+TagDecl *HeuristicResolver::resolveTypeToTagDecl(QualType T) const {
+  return HeuristicResolverImpl(Ctx).resolveTypeToTagDecl(T);
+}
 
 } // namespace clang

>From 4d36cdc893e1a1c74fece664441f21b129b25b6f Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 21:36:42 -0400
Subject: [PATCH 3/6] Expose simplifyType() publically in HeuristicResolver

---
 clang/include/clang/Sema/HeuristicResolver.h |  8 ++++++++
 clang/lib/Sema/HeuristicResolver.cpp         | 14 +++++---------
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h
index c971b9a6a7b51..df60d3359c6a6 100644
--- a/clang/include/clang/Sema/HeuristicResolver.h
+++ b/clang/include/clang/Sema/HeuristicResolver.h
@@ -85,6 +85,14 @@ class HeuristicResolver {
   // in which a member's name can be looked up.
   TagDecl *resolveTypeToTagDecl(QualType T) const;
 
+  // Simplify the type `Type`.
+  // `E` is the expression whose type `Type` is, if known. This sometimes
+  // contains information relevant to the type that's not stored in `Type`
+  // itself.
+  // If `UnwrapPointer` is true, exactly only pointer type will be unwrapped
+  // during simplification, and the operation fails if no pointer type is found.
+  QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
+
 private:
   ASTContext &Ctx;
 };
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 11fe786fa91c6..97b64372f3d93 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -48,6 +48,7 @@ class HeuristicResolverImpl {
   lookupDependentName(CXXRecordDecl *RD, DeclarationName Name,
                       llvm::function_ref<bool(const NamedDecl *ND)> Filter);
   TagDecl *resolveTypeToTagDecl(QualType T);
+  QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
 
 private:
   ASTContext &Ctx;
@@ -73,15 +74,6 @@ class HeuristicResolverImpl {
   QualType resolveExprToType(const Expr *E);
   std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
 
-  // Helper function for simplifying a type.
-  // `Type` is the type to simplify.
-  // `E` is the expression whose type `Type` is, if known. This sometimes
-  // contains information relevant to the type that's not stored in `Type`
-  // itself.
-  // If `UnwrapPointer` is true, exactly only pointer type will be unwrapped
-  // during simplification, and the operation fails if no pointer type is found.
-  QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
-
   bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
                                             CXXBasePath &Path,
                                             DeclarationName Name);
@@ -537,5 +529,9 @@ const QualType HeuristicResolver::getPointeeType(QualType T) const {
 TagDecl *HeuristicResolver::resolveTypeToTagDecl(QualType T) const {
   return HeuristicResolverImpl(Ctx).resolveTypeToTagDecl(T);
 }
+QualType HeuristicResolver::simplifyType(QualType Type, const Expr *E,
+                                         bool UnwrapPointer) {
+  return HeuristicResolverImpl(Ctx).simplifyType(Type, E, UnwrapPointer);
+}
 
 } // namespace clang

>From e315538dd4d08078ab3fb47d1fbbc9caf8e9ae82 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 03:08:18 -0400
Subject: [PATCH 4/6] Plumb a HeuristicResolver parameter into getAsRecordDec()

---
 clang/include/clang/Sema/Sema.h     |  4 +++-
 clang/lib/Parse/Parser.cpp          |  5 +++--
 clang/lib/Sema/SemaCodeComplete.cpp | 20 ++++++++++++--------
 3 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fdef57e84ee3d..89ce852b0a09f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -290,7 +290,8 @@ class FileNullabilityMap {
 /// parameter. This avoids updating the type on hot paths in the parser.
 class PreferredTypeBuilder {
 public:
-  PreferredTypeBuilder(bool Enabled) : Enabled(Enabled) {}
+  PreferredTypeBuilder(ASTContext *Ctx, bool Enabled)
+      : Ctx(Ctx), Enabled(Enabled) {}
 
   void enterCondition(Sema &S, SourceLocation Tok);
   void enterReturn(Sema &S, SourceLocation Tok);
@@ -336,6 +337,7 @@ class PreferredTypeBuilder {
   }
 
 private:
+  ASTContext *Ctx;
   bool Enabled;
   /// Start position of a token for which we store expected type.
   SourceLocation ExpectedLoc;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0710542f5e938..09e784a8e04de 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -52,8 +52,9 @@ IdentifierInfo *Parser::getSEHExceptKeyword() {
 }
 
 Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
-    : PP(pp), PreferredType(pp.isCodeCompletionEnabled()), Actions(actions),
-      Diags(PP.getDiagnostics()), GreaterThanIsOperator(true),
+    : PP(pp),
+      PreferredType(&actions.getASTContext(), pp.isCodeCompletionEnabled()),
+      Actions(actions), Diags(PP.getDiagnostics()), GreaterThanIsOperator(true),
       ColonIsSacred(false), InMessageExpression(false),
       TemplateParameterDepth(0), ParsingInObjCContainer(false) {
   SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies;
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index db467d76b5d32..37c2fefd6489b 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -436,7 +436,8 @@ void PreferredTypeBuilder::enterVariableInit(SourceLocation Tok, Decl *D) {
   ExpectedLoc = Tok;
 }
 
-static QualType getDesignatedType(QualType BaseType, const Designation &Desig);
+static QualType getDesignatedType(QualType BaseType, const Designation &Desig,
+                                  HeuristicResolver &Resolver);
 
 void PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
                                                       QualType BaseType,
@@ -444,7 +445,8 @@ void PreferredTypeBuilder::enterDesignatedInitializer(SourceLocation Tok,
   if (!Enabled)
     return;
   ComputeType = nullptr;
-  Type = getDesignatedType(BaseType, D);
+  HeuristicResolver Resolver(*Ctx);
+  Type = getDesignatedType(BaseType, D, Resolver);
   ExpectedLoc = Tok;
 }
 
@@ -5346,7 +5348,8 @@ AddRecordMembersCompletionResults(Sema &SemaRef, ResultBuilder &Results,
 // Returns the RecordDecl inside the BaseType, falling back to primary template
 // in case of specializations. Since we might not have a decl for the
 // instantiation/specialization yet, e.g. dependent code.
-static RecordDecl *getAsRecordDecl(QualType BaseType) {
+static RecordDecl *getAsRecordDecl(QualType BaseType,
+                                   HeuristicResolver &Resolver) {
   BaseType = BaseType.getNonReferenceType();
   if (auto *RD = BaseType->getAsRecordDecl()) {
     if (const auto *CTSD =
@@ -5911,7 +5914,7 @@ void SemaCodeCompletion::CodeCompleteMemberReferenceExpr(
       }
     }
 
-    if (RecordDecl *RD = getAsRecordDecl(BaseType)) {
+    if (RecordDecl *RD = getAsRecordDecl(BaseType, Resolver)) {
       AddRecordMembersCompletionResults(SemaRef, Results, S, BaseType, BaseKind,
                                         RD, std::move(AccessOpFixIt));
     } else if (const auto *TTPT =
@@ -6674,7 +6677,8 @@ QualType SemaCodeCompletion::ProduceTemplateArgumentSignatureHelp(
                               /*Braced=*/false);
 }
 
-static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
+static QualType getDesignatedType(QualType BaseType, const Designation &Desig,
+                                  HeuristicResolver &Resolver) {
   for (unsigned I = 0; I < Desig.getNumDesignators(); ++I) {
     if (BaseType.isNull())
       break;
@@ -6685,7 +6689,7 @@ static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
         NextType = BaseType->getAsArrayTypeUnsafe()->getElementType();
     } else {
       assert(D.isFieldDesignator());
-      auto *RD = getAsRecordDecl(BaseType);
+      auto *RD = getAsRecordDecl(BaseType, Resolver);
       if (RD && RD->isCompleteDefinition()) {
         for (const auto *Member : RD->lookup(D.getFieldDecl()))
           if (const FieldDecl *FD = llvm::dyn_cast<FieldDecl>(Member)) {
@@ -6701,10 +6705,10 @@ static QualType getDesignatedType(QualType BaseType, const Designation &Desig) {
 
 void SemaCodeCompletion::CodeCompleteDesignator(
     QualType BaseType, llvm::ArrayRef<Expr *> InitExprs, const Designation &D) {
-  BaseType = getDesignatedType(BaseType, D);
+  BaseType = getDesignatedType(BaseType, D, Resolver);
   if (BaseType.isNull())
     return;
-  const auto *RD = getAsRecordDecl(BaseType);
+  const auto *RD = getAsRecordDecl(BaseType, Resolver);
   if (!RD || RD->fields().empty())
     return;
 

>From bc87a07f211597dd194843fe278cb300a31864d9 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 03:08:54 -0400
Subject: [PATCH 5/6] Reimplement getAsRecordDecl() using HeuristicResolver

---
 clang/lib/Sema/SemaCodeComplete.cpp         | 23 +++------------------
 clang/test/CodeCompletion/member-access.cpp |  6 ++++--
 2 files changed, 7 insertions(+), 22 deletions(-)

diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 37c2fefd6489b..c20d47d079b9b 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -5350,26 +5350,9 @@ AddRecordMembersCompletionResults(Sema &SemaRef, ResultBuilder &Results,
 // instantiation/specialization yet, e.g. dependent code.
 static RecordDecl *getAsRecordDecl(QualType BaseType,
                                    HeuristicResolver &Resolver) {
-  BaseType = BaseType.getNonReferenceType();
-  if (auto *RD = BaseType->getAsRecordDecl()) {
-    if (const auto *CTSD =
-            llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
-      // Template might not be instantiated yet, fall back to primary template
-      // in such cases.
-      if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
-        RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
-    }
-    return RD;
-  }
-
-  if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
-    if (const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(
-            TST->getTemplateName().getAsTemplateDecl())) {
-      return TD->getTemplatedDecl();
-    }
-  }
-
-  return nullptr;
+  BaseType = Resolver.simplifyType(BaseType, nullptr, /*UnwrapPointer=*/false);
+  return dyn_cast_if_present<RecordDecl>(
+      Resolver.resolveTypeToTagDecl(BaseType));
 }
 
 namespace {
diff --git a/clang/test/CodeCompletion/member-access.cpp b/clang/test/CodeCompletion/member-access.cpp
index b181466cdb628..8526ed7273474 100644
--- a/clang/test/CodeCompletion/member-access.cpp
+++ b/clang/test/CodeCompletion/member-access.cpp
@@ -431,7 +431,9 @@ using Alias = S<T>;
 template <typename T>
 void f(Alias<T> s) {
   s.a.b;
-  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:433:7 %s -o - | FileCheck -check-prefix=CHECK-TEMPLATE-ALIAS %s
-  // CHECK-TEMPLATE-ALIAS: [#int#]b
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:433:5 %s -o - | FileCheck -check-prefix=CHECK-TEMPLATE-ALIAS %s
+  // CHECK-TEMPLATE-ALIAS: [#A#]a
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:433:7 %s -o - | FileCheck -check-prefix=CHECK-TEMPLATE-ALIAS-NESTED %s
+  // CHECK-TEMPLATE-ALIAS-NESTED: [#int#]b
 }
 }

>From 23a5c6db38f3abb00839d2387f3d070228e51a48 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Sun, 9 Mar 2025 21:54:52 -0400
Subject: [PATCH 6/6] Handle TSK_Undeclared in resolveTypeToTagDecl()

---
 clang/lib/Sema/HeuristicResolver.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 97b64372f3d93..d377379c627db 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -137,7 +137,15 @@ TagDecl *HeuristicResolverImpl::resolveTypeToTagDecl(QualType QT) {
   }
 
   if (auto *TT = T->getAs<TagType>()) {
-    return TT->getDecl();
+    TagDecl *TD = TT->getDecl();
+    // Template might not be instantiated yet, fall back to primary template
+    // in such cases.
+    if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(TD)) {
+      if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared) {
+        return CTSD->getSpecializedTemplate()->getTemplatedDecl();
+      }
+    }
+    return TD;
   }
 
   if (const auto *ICNT = T->getAs<InjectedClassNameType>())



More information about the cfe-commits mailing list