[clang] [clang-tools-extra] [clang][CodeComplete] Use HeuristicResolver to resolve pointee types (PR #121315)
Nathan Ridge via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 19 23:15:18 PST 2025
https://github.com/HighCommander4 updated https://github.com/llvm/llvm-project/pull/121315
>From 6cd526368a02fa2cf2e764eee295d356f73c3c1a Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Mon, 20 Jan 2025 01:52:24 -0500
Subject: [PATCH 1/3] Upgrade HeuristicResolver to use QualType rather than
Type*
---
clang-tools-extra/clangd/FindTarget.cpp | 3 +-
clang-tools-extra/clangd/XRefs.cpp | 7 +-
clang/include/clang/Sema/HeuristicResolver.h | 4 +-
clang/lib/Sema/HeuristicResolver.cpp | 77 ++++++++++----------
4 files changed, 47 insertions(+), 44 deletions(-)
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index 04fd6d437b7bdd..bb4c91b8313540 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -496,8 +496,7 @@ struct TargetFinder {
return;
case NestedNameSpecifier::Identifier:
if (Resolver) {
- add(QualType(Resolver->resolveNestedNameSpecifierToType(NNS), 0),
- Flags);
+ add(Resolver->resolveNestedNameSpecifierToType(NNS), Flags);
}
return;
case NestedNameSpecifier::TypeSpec:
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 0a093108b752c3..1a23f6cca77561 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -2034,9 +2034,10 @@ static void unwrapFindType(
// For smart pointer types, add the underlying type
if (H)
- if (const auto* PointeeType = H->getPointeeType(T.getNonReferenceType().getTypePtr())) {
- unwrapFindType(QualType(PointeeType, 0), H, Out);
- return Out.push_back(T);
+ if (auto PointeeType = H->getPointeeType(T.getNonReferenceType());
+ !PointeeType.isNull()) {
+ unwrapFindType(PointeeType, H, Out);
+ return Out.push_back(T);
}
return Out.push_back(T);
diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h
index 947de7a4e83ce0..3760003aab89f1 100644
--- a/clang/include/clang/Sema/HeuristicResolver.h
+++ b/clang/include/clang/Sema/HeuristicResolver.h
@@ -66,13 +66,13 @@ class HeuristicResolver {
// Try to heuristically resolve a dependent nested name specifier
// to the type it likely denotes. Note that *dependent* name specifiers always
// denote types, not namespaces.
- const Type *
+ QualType
resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) const;
// Given the type T of a dependent expression that appears of the LHS of a
// "->", heuristically find a corresponding pointee type in whose scope we
// could look up the name appearing on the RHS.
- const Type *getPointeeType(const Type *T) const;
+ const QualType getPointeeType(QualType T) const;
private:
ASTContext &Ctx;
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 7c1b8450b96330..f883b85a80c60f 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -42,8 +42,8 @@ class HeuristicResolverImpl {
resolveDependentNameType(const DependentNameType *DNT);
std::vector<const NamedDecl *> resolveTemplateSpecializationType(
const DependentTemplateSpecializationType *DTST);
- const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
- const Type *getPointeeType(const Type *T);
+ QualType resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
+ QualType getPointeeType(QualType T);
private:
ASTContext &Ctx;
@@ -61,12 +61,12 @@ class HeuristicResolverImpl {
// This heuristic will give the desired answer in many cases, e.g.
// for a call to vector<T>::size().
std::vector<const NamedDecl *>
- resolveDependentMember(const Type *T, DeclarationName Name,
+ resolveDependentMember(QualType T, DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
// Try to heuristically resolve the type of a possibly-dependent expression
// `E`.
- const Type *resolveExprToType(const Expr *E);
+ QualType resolveExprToType(const Expr *E);
std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
// Helper function for HeuristicResolver::resolveDependentMember()
@@ -104,17 +104,17 @@ const auto TemplateFilter = [](const NamedDecl *D) {
return isa<TemplateDecl>(D);
};
-const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
- ASTContext &Ctx) {
+QualType resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
+ ASTContext &Ctx) {
if (Decls.size() != 1) // Names an overload set -- just bail.
- return nullptr;
+ return QualType();
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
- return Ctx.getTypeDeclType(TD).getTypePtr();
+ return Ctx.getTypeDeclType(TD);
}
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
- return VD->getType().getTypePtrOrNull();
+ return VD->getType();
}
- return nullptr;
+ return QualType();
}
TemplateName getReferencedTemplateName(const Type *T) {
@@ -137,7 +137,8 @@ CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) {
T = T->getCanonicalTypeInternal().getTypePtr();
if (const auto *DNT = T->getAs<DependentNameType>()) {
- T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx);
+ T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx)
+ .getTypePtrOrNull();
if (!T)
return nullptr;
T = T->getCanonicalTypeInternal().getTypePtr();
@@ -163,12 +164,12 @@ CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) {
return TD->getTemplatedDecl();
}
-const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
- if (!T)
- return nullptr;
+QualType HeuristicResolverImpl::getPointeeType(QualType T) {
+ if (T.isNull())
+ return QualType();
if (T->isPointerType())
- return T->castAs<PointerType>()->getPointeeType().getTypePtrOrNull();
+ return T->castAs<PointerType>()->getPointeeType();
// Try to handle smart pointer types.
@@ -177,7 +178,7 @@ const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
auto ArrowOps = resolveDependentMember(
T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
if (ArrowOps.empty())
- return nullptr;
+ return QualType();
// Getting the return type of the found operator-> method decl isn't useful,
// because we discarded template arguments to perform lookup in the primary
@@ -187,13 +188,13 @@ const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
// form of SmartPtr<X, ...>, and assume X is the pointee type.
auto *TST = T->getAs<TemplateSpecializationType>();
if (!TST)
- return nullptr;
+ return QualType();
if (TST->template_arguments().size() == 0)
- return nullptr;
+ return QualType();
const TemplateArgument &FirstArg = TST->template_arguments()[0];
if (FirstArg.getKind() != TemplateArgument::Type)
- return nullptr;
- return FirstArg.getAsType().getTypePtrOrNull();
+ return QualType();
+ return FirstArg.getAsType();
}
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
@@ -210,7 +211,8 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
// with `this` as the base expression as `X` as the qualifier
// (which could be valid if `X` names a base class after instantiation).
if (NestedNameSpecifier *NNS = ME->getQualifier()) {
- if (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) {
+ if (QualType QualifierType = resolveNestedNameSpecifierToType(NNS);
+ !QualifierType.isNull()) {
auto Decls =
resolveDependentMember(QualifierType, ME->getMember(), NoFilter);
if (!Decls.empty())
@@ -225,11 +227,11 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
}
// Try resolving the member inside the expression's base type.
- const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
+ QualType BaseType = ME->getBaseType();
if (ME->isArrow()) {
BaseType = getPointeeType(BaseType);
}
- if (!BaseType)
+ if (BaseType.isNull())
return {};
if (const auto *BT = BaseType->getAs<BuiltinType>()) {
// If BaseType is the type of a dependent expression, it's just
@@ -245,17 +247,17 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
std::vector<const NamedDecl *>
HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) {
- return resolveDependentMember(RE->getQualifier()->getAsType(),
+ return resolveDependentMember(QualType(RE->getQualifier()->getAsType(), 0),
RE->getDeclName(), StaticFilter);
}
std::vector<const NamedDecl *>
HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
- const auto *CalleeType = resolveExprToType(CE->getCallee());
- if (!CalleeType)
+ QualType CalleeType = resolveExprToType(CE->getCallee());
+ if (CalleeType.isNull())
return {};
if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
- CalleeType = FnTypePtr->getPointeeType().getTypePtr();
+ CalleeType = FnTypePtr->getPointeeType();
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
if (const auto *D =
resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
@@ -276,7 +278,7 @@ HeuristicResolverImpl::resolveCalleeOfCallExpr(const CallExpr *CE) {
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveUsingValueDecl(
const UnresolvedUsingValueDecl *UUVD) {
- return resolveDependentMember(UUVD->getQualifier()->getAsType(),
+ return resolveDependentMember(QualType(UUVD->getQualifier()->getAsType(), 0),
UUVD->getNameInfo().getName(), ValueFilter);
}
@@ -317,18 +319,18 @@ HeuristicResolverImpl::resolveExprToDecls(const Expr *E) {
return {};
}
-const Type *HeuristicResolverImpl::resolveExprToType(const Expr *E) {
+QualType HeuristicResolverImpl::resolveExprToType(const Expr *E) {
std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
if (!Decls.empty())
return resolveDeclsToType(Decls, Ctx);
- return E->getType().getTypePtr();
+ return E->getType();
}
-const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
+QualType HeuristicResolverImpl::resolveNestedNameSpecifierToType(
const NestedNameSpecifier *NNS) {
if (!NNS)
- return nullptr;
+ return QualType();
// The purpose of this function is to handle the dependent (Kind ==
// Identifier) case, but we need to recurse on the prefix because
@@ -337,7 +339,7 @@ const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
switch (NNS->getKind()) {
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
- return NNS->getAsType();
+ return QualType(NNS->getAsType(), 0);
case NestedNameSpecifier::Identifier: {
return resolveDeclsToType(
resolveDependentMember(
@@ -348,7 +350,7 @@ const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
default:
break;
}
- return nullptr;
+ return QualType();
}
bool isOrdinaryMember(const NamedDecl *ND) {
@@ -410,8 +412,9 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::lookupDependentName(
}
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
- const Type *T, DeclarationName Name,
+ QualType QT, DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
+ const Type *T = QT.getTypePtrOrNull();
if (!T)
return {};
if (auto *ET = T->getAs<EnumType>()) {
@@ -457,11 +460,11 @@ HeuristicResolver::resolveTemplateSpecializationType(
const DependentTemplateSpecializationType *DTST) const {
return HeuristicResolverImpl(Ctx).resolveTemplateSpecializationType(DTST);
}
-const Type *HeuristicResolver::resolveNestedNameSpecifierToType(
+QualType HeuristicResolver::resolveNestedNameSpecifierToType(
const NestedNameSpecifier *NNS) const {
return HeuristicResolverImpl(Ctx).resolveNestedNameSpecifierToType(NNS);
}
-const Type *HeuristicResolver::getPointeeType(const Type *T) const {
+const QualType HeuristicResolver::getPointeeType(QualType T) const {
return HeuristicResolverImpl(Ctx).getPointeeType(T);
}
>From f261eaf6a1c258f6825ce6a88cb7bdeabd3ad0c5 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Mon, 20 Jan 2025 01:57:42 -0500
Subject: [PATCH 2/3] Respect const-qualification on methods in
resolveDependentMember()
---
clang/lib/Sema/HeuristicResolver.cpp | 10 +++++++++-
.../unittests/Sema/HeuristicResolverTest.cpp | 20 +++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index f883b85a80c60f..fbce1a026be61c 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -425,7 +425,15 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
if (!RD->hasDefinition())
return {};
RD = RD->getDefinition();
- return lookupDependentName(RD, Name, Filter);
+ return lookupDependentName(RD, Name, [&](const NamedDecl *ND) {
+ if (!Filter(ND))
+ return false;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(ND)) {
+ if (QT.isConstQualified() && !MD->isConst())
+ return false;
+ }
+ return true;
+ });
}
return {};
}
diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp
index c4f054683ccdc9..2cd5486b3227f0 100644
--- a/clang/unittests/Sema/HeuristicResolverTest.cpp
+++ b/clang/unittests/Sema/HeuristicResolverTest.cpp
@@ -135,6 +135,26 @@ TEST(HeuristicResolver, MemberExpr_SmartPointer) {
cxxMethodDecl(hasName("foo")).bind("output"));
}
+TEST(HeuristicResolver, MemberExpr_SmartPointer_Qualified) {
+ std::string Code = R"cpp(
+ template <typename> struct Waldo {
+ void find();
+ void find() const;
+ };
+ template <typename T> struct unique_ptr {
+ T* operator->();
+ };
+ template <typename T>
+ void test(unique_ptr<const Waldo<T>>& w) {
+ w->find();
+ }
+ )cpp";
+ expectResolution(
+ Code, &HeuristicResolver::resolveMemberExpr,
+ cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
+ cxxMethodDecl(hasName("find"), isConst()).bind("output"));
+}
+
TEST(HeuristicResolver, MemberExpr_Chained) {
std::string Code = R"cpp(
struct A { void foo() {} };
>From fd71bd30aaf33d28b4fe96ee4edb62afd966bc94 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Thu, 26 Dec 2024 20:59:06 -0500
Subject: [PATCH 3/3] [clang][CodeComplete] Use HeuristicResolver to resolve
pointee types
Fixes https://github.com/clangd/clangd/issues/810
---
clang/include/clang/Sema/SemaCodeCompletion.h | 2 ++
clang/lib/Sema/SemaCodeComplete.cpp | 15 ++++++++++-----
clang/test/CodeCompletion/member-access.cpp | 17 +++++++++++++++++
3 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Sema/SemaCodeCompletion.h b/clang/include/clang/Sema/SemaCodeCompletion.h
index 50409439389b06..e931596c215d31 100644
--- a/clang/include/clang/Sema/SemaCodeCompletion.h
+++ b/clang/include/clang/Sema/SemaCodeCompletion.h
@@ -23,6 +23,7 @@
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Designator.h"
+#include "clang/Sema/HeuristicResolver.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/SemaBase.h"
#include "llvm/ADT/StringRef.h"
@@ -43,6 +44,7 @@ class SemaCodeCompletion : public SemaBase {
/// Code-completion consumer.
CodeCompleteConsumer *CodeCompleter;
+ HeuristicResolver Resolver;
/// Describes the context in which code completion occurs.
enum ParserCompletionContext {
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 8a848df70cc5a1..69cda6e68bd36b 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -34,6 +34,7 @@
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Designator.h"
+#include "clang/Sema/HeuristicResolver.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ParsedAttr.h"
@@ -5861,8 +5862,10 @@ void SemaCodeCompletion::CodeCompleteMemberReferenceExpr(
enum CodeCompletionContext::Kind contextKind;
if (IsArrow) {
- if (const auto *Ptr = ConvertedBaseType->getAs<PointerType>())
- ConvertedBaseType = Ptr->getPointeeType();
+ if (QualType PointeeType = Resolver.getPointeeType(ConvertedBaseType);
+ !PointeeType.isNull()) {
+ ConvertedBaseType = PointeeType;
+ }
}
if (IsArrow) {
@@ -5899,8 +5902,9 @@ void SemaCodeCompletion::CodeCompleteMemberReferenceExpr(
ExprValueKind BaseKind = Base->getValueKind();
if (IsArrow) {
- if (const PointerType *Ptr = BaseType->getAs<PointerType>()) {
- BaseType = Ptr->getPointeeType();
+ if (QualType PointeeType = Resolver.getPointeeType(BaseType);
+ !PointeeType.isNull()) {
+ BaseType = PointeeType;
BaseKind = VK_LValue;
} else if (BaseType->isObjCObjectPointerType() ||
BaseType->isTemplateTypeParmType()) {
@@ -10472,4 +10476,5 @@ void SemaCodeCompletion::GatherGlobalCodeCompletions(
SemaCodeCompletion::SemaCodeCompletion(Sema &S,
CodeCompleteConsumer *CompletionConsumer)
- : SemaBase(S), CodeCompleter(CompletionConsumer) {}
+ : SemaBase(S), CodeCompleter(CompletionConsumer),
+ Resolver(S.getASTContext()) {}
diff --git a/clang/test/CodeCompletion/member-access.cpp b/clang/test/CodeCompletion/member-access.cpp
index 912f269db6c1ac..ab6dc69bf2923d 100644
--- a/clang/test/CodeCompletion/member-access.cpp
+++ b/clang/test/CodeCompletion/member-access.cpp
@@ -384,3 +384,20 @@ void Foo() {
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:382:5 %s -o - | FileCheck -check-prefix=CHECK-DEREF-DEPENDENT %s
// CHECK-DEREF-DEPENDENT: [#void#]Add()
}
+
+namespace dependent_smart_pointer {
+template <typename T>
+struct smart_pointer {
+ T* operator->();
+};
+
+template <typename T>
+struct node {
+ smart_pointer<node<T>> next;
+ void foo() {
+ next->next;
+ // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:398:11 %s -o - | FileCheck -check-prefix=CHECK-DEPENDENT-SMARTPTR %s
+ // CHECK-DEPENDENT-SMARTPTR: [#smart_pointer<node<T>>#]next
+ }
+};
+}
More information about the cfe-commits
mailing list