-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[clang][Sema] Respect const-qualification of methods in heuristic results #123551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][Sema] Respect const-qualification of methods in heuristic results #123551
Conversation
@llvm/pr-subscribers-clang Author: Nathan Ridge (HighCommander4) ChangesFixes #123549 Full diff: https://github.com/llvm/llvm-project/pull/123551.diff 5 Files Affected:
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..fbce1a026be61c 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>()) {
@@ -422,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 {};
}
@@ -457,11 +468,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);
}
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() {} };
|
@llvm/pr-subscribers-clang-tools-extra Author: Nathan Ridge (HighCommander4) ChangesFixes #123549 Full diff: https://github.com/llvm/llvm-project/pull/123551.diff 5 Files Affected:
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..fbce1a026be61c 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>()) {
@@ -422,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 {};
}
@@ -457,11 +468,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);
}
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() {} };
|
@llvm/pr-subscribers-clangd Author: Nathan Ridge (HighCommander4) ChangesFixes #123549 Full diff: https://github.com/llvm/llvm-project/pull/123551.diff 5 Files Affected:
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..fbce1a026be61c 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>()) {
@@ -422,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 {};
}
@@ -457,11 +468,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);
}
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() {} };
|
For ease of review, I split the PR into two patches (which I plan to squash):
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a thought from my side, but not required at the moment because this aims to be as simple as possible. Otherwise LGTM
clang/lib/Sema/HeuristicResolver.cpp
Outdated
if (QT.isConstQualified() && !MD->isConst()) | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also have isVolatileQualfied()/isRestrictQualified()
... do we also want to handle them?
With that in mind, I'm also thinking if we can reuse something from Sema e.g. we have hasInconsistentOrSupersetQualifiersOf()
that does the similar job in SemaTemplateDeduction.cpp that you might want to take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion!
After some poking around I found Qualifiers::compatiblyIncludes()
which seems to fit the purpose and is used in a number of similar situations (e.g. in checkPointerTypesForAssignment()
).
c623e7e
to
aec579f
Compare
Fixes #123549