[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