[clang] [clang][CodeComplete] Use HeuristicResolver in getAsRecordDecl() (PR #130473)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 9 19:45:26 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Nathan Ridge (HighCommander4)
<details>
<summary>Changes</summary>
Fixes https://github.com/llvm/llvm-project/issues/130468
---
Full diff: https://github.com/llvm/llvm-project/pull/130473.diff
6 Files Affected:
- (modified) clang/include/clang/Sema/HeuristicResolver.h (+12)
- (modified) clang/include/clang/Sema/Sema.h (+3-1)
- (modified) clang/lib/Parse/Parser.cpp (+3-2)
- (modified) clang/lib/Sema/HeuristicResolver.cpp (+25-24)
- (modified) clang/lib/Sema/SemaCodeComplete.cpp (+15-28)
- (modified) clang/test/CodeCompletion/member-access.cpp (+4-2)
``````````diff
diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h
index f511815b40199..df60d3359c6a6 100644
--- a/clang/include/clang/Sema/HeuristicResolver.h
+++ b/clang/include/clang/Sema/HeuristicResolver.h
@@ -81,6 +81,18 @@ 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;
+
+ // 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/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/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 7aecd2a73b539..d377379c627db 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -47,6 +47,8 @@ class HeuristicResolverImpl {
std::vector<const NamedDecl *>
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;
@@ -72,20 +74,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(const Type *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
- // 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);
@@ -132,8 +120,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();
@@ -147,7 +137,15 @@ TagDecl *HeuristicResolverImpl::resolveTypeToTagDecl(const Type *T) {
}
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>())
@@ -315,8 +313,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 +424,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 +467,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)) {
@@ -540,5 +534,12 @@ 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);
+}
+QualType HeuristicResolver::simplifyType(QualType Type, const Expr *E,
+ bool UnwrapPointer) {
+ return HeuristicResolverImpl(Ctx).simplifyType(Type, E, UnwrapPointer);
+}
} // namespace clang
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index db467d76b5d32..c20d47d079b9b 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,27 +5348,11 @@ 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) {
- 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;
+static RecordDecl *getAsRecordDecl(QualType BaseType,
+ HeuristicResolver &Resolver) {
+ BaseType = Resolver.simplifyType(BaseType, nullptr, /*UnwrapPointer=*/false);
+ return dyn_cast_if_present<RecordDecl>(
+ Resolver.resolveTypeToTagDecl(BaseType));
}
namespace {
@@ -5911,7 +5897,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 +6660,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 +6672,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 +6688,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;
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
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/130473
More information about the cfe-commits
mailing list