Skip to content

Commit 842f25a

Browse files
authored
[NFC] [dataflow] generalize smart pointer caching (#133350)
This allows us to use it for classes that use other names than get / value.
1 parent 8c18c25 commit 842f25a

File tree

3 files changed

+120
-74
lines changed

3 files changed

+120
-74
lines changed

clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ ast_matchers::StatementMatcher isPointerLikeOperatorStar();
6262
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
6363
ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
6464
ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
65-
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
66-
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();
65+
ast_matchers::StatementMatcher
66+
isSmartPointerLikeValueMethodCall(clang::StringRef MethodName = "value");
67+
ast_matchers::StatementMatcher
68+
isSmartPointerLikeGetMethodCall(clang::StringRef MethodName = "get");
6769

6870
// Common transfer functions.
6971

clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "clang/AST/CanonicalType.h"
44
#include "clang/AST/DeclCXX.h"
5+
#include "clang/AST/Type.h"
56
#include "clang/ASTMatchers/ASTMatchers.h"
67
#include "clang/ASTMatchers/ASTMatchersMacros.h"
78
#include "clang/Basic/OperatorKinds.h"
@@ -23,13 +24,28 @@ using ast_matchers::pointerType;
2324
using ast_matchers::referenceType;
2425
using ast_matchers::returns;
2526

26-
bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
27-
bool &HasValue) {
27+
CanQualType getLikeReturnType(QualType RT) {
28+
if (!RT.isNull() && RT->isPointerType()) {
29+
return RT->getPointeeType()
30+
->getCanonicalTypeUnqualified()
31+
.getUnqualifiedType();
32+
}
33+
return {};
34+
}
35+
36+
CanQualType valueLikeReturnType(QualType RT) {
37+
if (!RT.isNull() && RT->isReferenceType()) {
38+
return RT.getNonReferenceType()
39+
->getCanonicalTypeUnqualified()
40+
.getUnqualifiedType();
41+
}
42+
return {};
43+
}
44+
45+
CanQualType pointerLikeReturnType(const CXXRecordDecl &RD) {
2846
// We may want to cache this search, but in current profiles it hasn't shown
2947
// up as a hot spot (possibly because there aren't many hits, relatively).
30-
bool HasArrow = false;
31-
bool HasStar = false;
32-
CanQualType StarReturnType, ArrowReturnType, GetReturnType, ValueReturnType;
48+
CanQualType StarReturnType, ArrowReturnType;
3349
for (const auto *MD : RD.methods()) {
3450
// We only consider methods that are const and have zero parameters.
3551
// It may be that there is a non-const overload for the method, but
@@ -38,55 +54,35 @@ bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
3854
continue;
3955
switch (MD->getOverloadedOperator()) {
4056
case OO_Star:
41-
if (MD->getReturnType()->isReferenceType()) {
42-
HasStar = true;
43-
StarReturnType = MD->getReturnType()
44-
.getNonReferenceType()
45-
->getCanonicalTypeUnqualified()
46-
.getUnqualifiedType();
47-
}
57+
StarReturnType = valueLikeReturnType(MD->getReturnType());
4858
break;
4959
case OO_Arrow:
50-
if (MD->getReturnType()->isPointerType()) {
51-
HasArrow = true;
52-
ArrowReturnType = MD->getReturnType()
53-
->getPointeeType()
54-
->getCanonicalTypeUnqualified()
55-
.getUnqualifiedType();
56-
}
60+
ArrowReturnType = getLikeReturnType(MD->getReturnType());
5761
break;
58-
case OO_None: {
59-
IdentifierInfo *II = MD->getIdentifier();
60-
if (II == nullptr)
61-
continue;
62-
if (II->isStr("get")) {
63-
if (MD->getReturnType()->isPointerType()) {
64-
HasGet = true;
65-
GetReturnType = MD->getReturnType()
66-
->getPointeeType()
67-
->getCanonicalTypeUnqualified()
68-
.getUnqualifiedType();
69-
}
70-
} else if (II->isStr("value")) {
71-
if (MD->getReturnType()->isReferenceType()) {
72-
HasValue = true;
73-
ValueReturnType = MD->getReturnType()
74-
.getNonReferenceType()
75-
->getCanonicalTypeUnqualified()
76-
.getUnqualifiedType();
77-
}
78-
}
79-
} break;
8062
default:
8163
break;
8264
}
8365
}
66+
if (!StarReturnType.isNull() && !ArrowReturnType.isNull() &&
67+
StarReturnType == ArrowReturnType)
68+
return StarReturnType;
8469

85-
if (!HasStar || !HasArrow || StarReturnType != ArrowReturnType)
86-
return false;
87-
HasGet = HasGet && (GetReturnType == StarReturnType);
88-
HasValue = HasValue && (ValueReturnType == StarReturnType);
89-
return true;
70+
return {};
71+
}
72+
73+
QualType findReturnType(const CXXRecordDecl &RD, StringRef MethodName) {
74+
for (const auto *MD : RD.methods()) {
75+
// We only consider methods that are const and have zero parameters.
76+
// It may be that there is a non-const overload for the method, but
77+
// there should at least be a const overload as well.
78+
if (!MD->isConst() || MD->getNumParams() != 0 ||
79+
MD->getOverloadedOperator() != OO_None)
80+
continue;
81+
clang::IdentifierInfo *II = MD->getIdentifier();
82+
if (II && II->isStr(MethodName))
83+
return MD->getReturnType();
84+
}
85+
return {};
9086
}
9187

9288
} // namespace
@@ -96,36 +92,37 @@ bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
9692
// its own anonymous namespace instead of in clang::dataflow.
9793
namespace {
9894

99-
AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGet) {
100-
bool HasGet = false;
101-
bool HasValue = false;
102-
bool HasStarAndArrow =
103-
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
104-
return HasStarAndArrow && HasGet;
95+
using clang::dataflow::findReturnType;
96+
using clang::dataflow::getLikeReturnType;
97+
using clang::dataflow::pointerLikeReturnType;
98+
using clang::dataflow::valueLikeReturnType;
99+
100+
AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithGetLike,
101+
clang::StringRef, MethodName) {
102+
auto RT = pointerLikeReturnType(Node);
103+
if (RT.isNull())
104+
return false;
105+
return getLikeReturnType(findReturnType(Node, MethodName)) == RT;
105106
}
106107

107-
AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithValue) {
108-
bool HasGet = false;
109-
bool HasValue = false;
110-
bool HasStarAndArrow =
111-
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
112-
return HasStarAndArrow && HasValue;
108+
AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike,
109+
clang::StringRef, MethodName) {
110+
auto RT = pointerLikeReturnType(Node);
111+
if (RT.isNull())
112+
return false;
113+
return valueLikeReturnType(findReturnType(Node, MethodName)) == RT;
113114
}
114115

115116
AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
116-
bool HasGet = false;
117-
bool HasValue = false;
118-
bool HasStarAndArrow =
119-
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
120-
return HasStarAndArrow && (HasGet || HasValue);
117+
auto RT = pointerLikeReturnType(Node);
118+
if (RT.isNull())
119+
return false;
120+
return getLikeReturnType(findReturnType(Node, "get")) == RT ||
121+
valueLikeReturnType(findReturnType(Node, "value")) == RT;
121122
}
122123

123124
AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
124-
bool HasGet = false;
125-
bool HasValue = false;
126-
bool HasStarAndArrow =
127-
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
128-
return HasStarAndArrow;
125+
return !pointerLikeReturnType(Node).isNull();
129126
}
130127

131128
} // namespace
@@ -164,16 +161,19 @@ ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {
164161
ofClass(pointerClass()))));
165162
}
166163

167-
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
164+
ast_matchers::StatementMatcher
165+
isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) {
168166
return cxxMemberCallExpr(callee(cxxMethodDecl(
169167
parameterCountIs(0), returns(hasCanonicalType(referenceType())),
170-
hasName("value"), ofClass(smartPointerClassWithValue()))));
168+
hasName(MethodName),
169+
ofClass(smartPointerClassWithValueLike(MethodName)))));
171170
}
172171

173-
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall() {
172+
ast_matchers::StatementMatcher
173+
isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) {
174174
return cxxMemberCallExpr(callee(cxxMethodDecl(
175175
parameterCountIs(0), returns(hasCanonicalType(pointerType())),
176-
hasName("get"), ofClass(smartPointerClassWithGet()))));
176+
hasName(MethodName), ofClass(smartPointerClassWithGetLike(MethodName)))));
177177
}
178178

179179
const FunctionDecl *

clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,5 +348,49 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
348348
isSmartPointerLikeGetMethodCall()));
349349
}
350350

351+
TEST(SmartPointerAccessorCachingTest, Renamed) {
352+
llvm::StringRef Decls(R"cc(
353+
namespace std {
354+
template <class T>
355+
struct unique_ptr {
356+
T* operator->() const;
357+
T& operator*() const;
358+
T* Get() const;
359+
T& Value() const;
360+
};
361+
} // namespace std
362+
363+
template <class T>
364+
using UniquePtrAlias = std::unique_ptr<T>;
365+
366+
struct S { int i; };
367+
)cc");
368+
369+
EXPECT_TRUE(matches(Decls,
370+
"int target(std::unique_ptr<S> P) { return (*P).i; }",
371+
isPointerLikeOperatorStar()));
372+
373+
EXPECT_TRUE(matches(Decls,
374+
"int target(std::unique_ptr<S> P) { return P->i; }",
375+
isPointerLikeOperatorArrow()));
376+
377+
EXPECT_TRUE(matches(Decls,
378+
"int target(std::unique_ptr<S> P) { return P.Get()->i; }",
379+
isSmartPointerLikeGetMethodCall("Get")));
380+
EXPECT_TRUE(matches(Decls,
381+
"int target(UniquePtrAlias<S> P) { return P.Get()->i; }",
382+
isSmartPointerLikeGetMethodCall("Get")));
383+
384+
EXPECT_TRUE(
385+
matches(Decls, "int target(std::unique_ptr<S> P) { return P.Value().i; }",
386+
isSmartPointerLikeValueMethodCall("Value")));
387+
EXPECT_TRUE(matches(Decls,
388+
"int target(UniquePtrAlias<S> P) { return P.Value().i; }",
389+
isSmartPointerLikeValueMethodCall("Value")));
390+
391+
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
392+
isPointerLikeOperatorArrow()));
393+
}
394+
351395
} // namespace
352396
} // namespace clang::dataflow

0 commit comments

Comments
 (0)