Skip to content

Commit 4740e09

Browse files
[clang][Sema] Respect qualification of methods in heuristic results (#123551)
Fixes #123549
1 parent 97d691b commit 4740e09

File tree

5 files changed

+76
-45
lines changed

5 files changed

+76
-45
lines changed

clang-tools-extra/clangd/FindTarget.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,7 @@ struct TargetFinder {
496496
return;
497497
case NestedNameSpecifier::Identifier:
498498
if (Resolver) {
499-
add(QualType(Resolver->resolveNestedNameSpecifierToType(NNS), 0),
500-
Flags);
499+
add(Resolver->resolveNestedNameSpecifierToType(NNS), Flags);
501500
}
502501
return;
503502
case NestedNameSpecifier::TypeSpec:

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,9 +2034,10 @@ static void unwrapFindType(
20342034

20352035
// For smart pointer types, add the underlying type
20362036
if (H)
2037-
if (const auto* PointeeType = H->getPointeeType(T.getNonReferenceType().getTypePtr())) {
2038-
unwrapFindType(QualType(PointeeType, 0), H, Out);
2039-
return Out.push_back(T);
2037+
if (auto PointeeType = H->getPointeeType(T.getNonReferenceType());
2038+
!PointeeType.isNull()) {
2039+
unwrapFindType(PointeeType, H, Out);
2040+
return Out.push_back(T);
20402041
}
20412042

20422043
return Out.push_back(T);

clang/include/clang/Sema/HeuristicResolver.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ class HeuristicResolver {
6666
// Try to heuristically resolve a dependent nested name specifier
6767
// to the type it likely denotes. Note that *dependent* name specifiers always
6868
// denote types, not namespaces.
69-
const Type *
69+
QualType
7070
resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) const;
7171

7272
// Given the type T of a dependent expression that appears of the LHS of a
7373
// "->", heuristically find a corresponding pointee type in whose scope we
7474
// could look up the name appearing on the RHS.
75-
const Type *getPointeeType(const Type *T) const;
75+
const QualType getPointeeType(QualType T) const;
7676

7777
private:
7878
ASTContext &Ctx;

clang/lib/Sema/HeuristicResolver.cpp

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ class HeuristicResolverImpl {
4242
resolveDependentNameType(const DependentNameType *DNT);
4343
std::vector<const NamedDecl *> resolveTemplateSpecializationType(
4444
const DependentTemplateSpecializationType *DTST);
45-
const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
46-
const Type *getPointeeType(const Type *T);
45+
QualType resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
46+
QualType getPointeeType(QualType T);
4747

4848
private:
4949
ASTContext &Ctx;
@@ -61,12 +61,12 @@ class HeuristicResolverImpl {
6161
// This heuristic will give the desired answer in many cases, e.g.
6262
// for a call to vector<T>::size().
6363
std::vector<const NamedDecl *>
64-
resolveDependentMember(const Type *T, DeclarationName Name,
64+
resolveDependentMember(QualType T, DeclarationName Name,
6565
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
6666

6767
// Try to heuristically resolve the type of a possibly-dependent expression
6868
// `E`.
69-
const Type *resolveExprToType(const Expr *E);
69+
QualType resolveExprToType(const Expr *E);
7070
std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
7171

7272
// Helper function for HeuristicResolver::resolveDependentMember()
@@ -104,17 +104,17 @@ const auto TemplateFilter = [](const NamedDecl *D) {
104104
return isa<TemplateDecl>(D);
105105
};
106106

107-
const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
108-
ASTContext &Ctx) {
107+
QualType resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
108+
ASTContext &Ctx) {
109109
if (Decls.size() != 1) // Names an overload set -- just bail.
110-
return nullptr;
110+
return QualType();
111111
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
112-
return Ctx.getTypeDeclType(TD).getTypePtr();
112+
return Ctx.getTypeDeclType(TD);
113113
}
114114
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
115-
return VD->getType().getTypePtrOrNull();
115+
return VD->getType();
116116
}
117-
return nullptr;
117+
return QualType();
118118
}
119119

120120
TemplateName getReferencedTemplateName(const Type *T) {
@@ -137,7 +137,8 @@ CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) {
137137
T = T->getCanonicalTypeInternal().getTypePtr();
138138

139139
if (const auto *DNT = T->getAs<DependentNameType>()) {
140-
T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx);
140+
T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx)
141+
.getTypePtrOrNull();
141142
if (!T)
142143
return nullptr;
143144
T = T->getCanonicalTypeInternal().getTypePtr();
@@ -163,12 +164,12 @@ CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) {
163164
return TD->getTemplatedDecl();
164165
}
165166

166-
const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
167-
if (!T)
168-
return nullptr;
167+
QualType HeuristicResolverImpl::getPointeeType(QualType T) {
168+
if (T.isNull())
169+
return QualType();
169170

170171
if (T->isPointerType())
171-
return T->castAs<PointerType>()->getPointeeType().getTypePtrOrNull();
172+
return T->castAs<PointerType>()->getPointeeType();
172173

173174
// Try to handle smart pointer types.
174175

@@ -177,7 +178,7 @@ const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
177178
auto ArrowOps = resolveDependentMember(
178179
T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
179180
if (ArrowOps.empty())
180-
return nullptr;
181+
return QualType();
181182

182183
// Getting the return type of the found operator-> method decl isn't useful,
183184
// because we discarded template arguments to perform lookup in the primary
@@ -187,13 +188,13 @@ const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
187188
// form of SmartPtr<X, ...>, and assume X is the pointee type.
188189
auto *TST = T->getAs<TemplateSpecializationType>();
189190
if (!TST)
190-
return nullptr;
191+
return QualType();
191192
if (TST->template_arguments().size() == 0)
192-
return nullptr;
193+
return QualType();
193194
const TemplateArgument &FirstArg = TST->template_arguments()[0];
194195
if (FirstArg.getKind() != TemplateArgument::Type)
195-
return nullptr;
196-
return FirstArg.getAsType().getTypePtrOrNull();
196+
return QualType();
197+
return FirstArg.getAsType();
197198
}
198199

199200
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
@@ -210,7 +211,8 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
210211
// with `this` as the base expression as `X` as the qualifier
211212
// (which could be valid if `X` names a base class after instantiation).
212213
if (NestedNameSpecifier *NNS = ME->getQualifier()) {
213-
if (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) {
214+
if (QualType QualifierType = resolveNestedNameSpecifierToType(NNS);
215+
!QualifierType.isNull()) {
214216
auto Decls =
215217
resolveDependentMember(QualifierType, ME->getMember(), NoFilter);
216218
if (!Decls.empty())
@@ -225,11 +227,11 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
225227
}
226228

227229
// Try resolving the member inside the expression's base type.
228-
const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
230+
QualType BaseType = ME->getBaseType();
229231
if (ME->isArrow()) {
230232
BaseType = getPointeeType(BaseType);
231233
}
232-
if (!BaseType)
234+
if (BaseType.isNull())
233235
return {};
234236
if (const auto *BT = BaseType->getAs<BuiltinType>()) {
235237
// If BaseType is the type of a dependent expression, it's just
@@ -245,17 +247,17 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
245247

246248
std::vector<const NamedDecl *>
247249
HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) {
248-
return resolveDependentMember(RE->getQualifier()->getAsType(),
250+
return resolveDependentMember(QualType(RE->getQualifier()->getAsType(), 0),
249251
RE->getDeclName(), StaticFilter);
250252
}
251253

252254
std::vector<const NamedDecl *>
253255
HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
254-
const auto *CalleeType = resolveExprToType(CE->getCallee());
255-
if (!CalleeType)
256+
QualType CalleeType = resolveExprToType(CE->getCallee());
257+
if (CalleeType.isNull())
256258
return {};
257259
if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
258-
CalleeType = FnTypePtr->getPointeeType().getTypePtr();
260+
CalleeType = FnTypePtr->getPointeeType();
259261
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
260262
if (const auto *D =
261263
resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
@@ -276,7 +278,7 @@ HeuristicResolverImpl::resolveCalleeOfCallExpr(const CallExpr *CE) {
276278

277279
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveUsingValueDecl(
278280
const UnresolvedUsingValueDecl *UUVD) {
279-
return resolveDependentMember(UUVD->getQualifier()->getAsType(),
281+
return resolveDependentMember(QualType(UUVD->getQualifier()->getAsType(), 0),
280282
UUVD->getNameInfo().getName(), ValueFilter);
281283
}
282284

@@ -317,18 +319,18 @@ HeuristicResolverImpl::resolveExprToDecls(const Expr *E) {
317319
return {};
318320
}
319321

320-
const Type *HeuristicResolverImpl::resolveExprToType(const Expr *E) {
322+
QualType HeuristicResolverImpl::resolveExprToType(const Expr *E) {
321323
std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
322324
if (!Decls.empty())
323325
return resolveDeclsToType(Decls, Ctx);
324326

325-
return E->getType().getTypePtr();
327+
return E->getType();
326328
}
327329

328-
const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
330+
QualType HeuristicResolverImpl::resolveNestedNameSpecifierToType(
329331
const NestedNameSpecifier *NNS) {
330332
if (!NNS)
331-
return nullptr;
333+
return QualType();
332334

333335
// The purpose of this function is to handle the dependent (Kind ==
334336
// Identifier) case, but we need to recurse on the prefix because
@@ -337,7 +339,7 @@ const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
337339
switch (NNS->getKind()) {
338340
case NestedNameSpecifier::TypeSpec:
339341
case NestedNameSpecifier::TypeSpecWithTemplate:
340-
return NNS->getAsType();
342+
return QualType(NNS->getAsType(), 0);
341343
case NestedNameSpecifier::Identifier: {
342344
return resolveDeclsToType(
343345
resolveDependentMember(
@@ -348,7 +350,7 @@ const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
348350
default:
349351
break;
350352
}
351-
return nullptr;
353+
return QualType();
352354
}
353355

354356
bool isOrdinaryMember(const NamedDecl *ND) {
@@ -410,8 +412,9 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::lookupDependentName(
410412
}
411413

412414
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
413-
const Type *T, DeclarationName Name,
415+
QualType QT, DeclarationName Name,
414416
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
417+
const Type *T = QT.getTypePtrOrNull();
415418
if (!T)
416419
return {};
417420
if (auto *ET = T->getAs<EnumType>()) {
@@ -422,7 +425,15 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
422425
if (!RD->hasDefinition())
423426
return {};
424427
RD = RD->getDefinition();
425-
return lookupDependentName(RD, Name, Filter);
428+
return lookupDependentName(RD, Name, [&](const NamedDecl *ND) {
429+
if (!Filter(ND))
430+
return false;
431+
if (const auto *MD = dyn_cast<CXXMethodDecl>(ND)) {
432+
return MD->getMethodQualifiers().compatiblyIncludes(QT.getQualifiers(),
433+
Ctx);
434+
}
435+
return true;
436+
});
426437
}
427438
return {};
428439
}
@@ -457,11 +468,11 @@ HeuristicResolver::resolveTemplateSpecializationType(
457468
const DependentTemplateSpecializationType *DTST) const {
458469
return HeuristicResolverImpl(Ctx).resolveTemplateSpecializationType(DTST);
459470
}
460-
const Type *HeuristicResolver::resolveNestedNameSpecifierToType(
471+
QualType HeuristicResolver::resolveNestedNameSpecifierToType(
461472
const NestedNameSpecifier *NNS) const {
462473
return HeuristicResolverImpl(Ctx).resolveNestedNameSpecifierToType(NNS);
463474
}
464-
const Type *HeuristicResolver::getPointeeType(const Type *T) const {
475+
const QualType HeuristicResolver::getPointeeType(QualType T) const {
465476
return HeuristicResolverImpl(Ctx).getPointeeType(T);
466477
}
467478

clang/unittests/Sema/HeuristicResolverTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,26 @@ TEST(HeuristicResolver, MemberExpr_SmartPointer) {
135135
cxxMethodDecl(hasName("foo")).bind("output"));
136136
}
137137

138+
TEST(HeuristicResolver, MemberExpr_SmartPointer_Qualified) {
139+
std::string Code = R"cpp(
140+
template <typename> struct Waldo {
141+
void find();
142+
void find() const;
143+
};
144+
template <typename T> struct unique_ptr {
145+
T* operator->();
146+
};
147+
template <typename T>
148+
void test(unique_ptr<const Waldo<T>>& w) {
149+
w->find();
150+
}
151+
)cpp";
152+
expectResolution(
153+
Code, &HeuristicResolver::resolveMemberExpr,
154+
cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
155+
cxxMethodDecl(hasName("find"), isConst()).bind("output"));
156+
}
157+
138158
TEST(HeuristicResolver, MemberExpr_Chained) {
139159
std::string Code = R"cpp(
140160
struct A { void foo() {} };

0 commit comments

Comments
 (0)