1 | #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" |
2 | |
3 | #include "clang/AST/CanonicalType.h" |
4 | #include "clang/AST/DeclCXX.h" |
5 | #include "clang/AST/Type.h" |
6 | #include "clang/ASTMatchers/ASTMatchers.h" |
7 | #include "clang/ASTMatchers/ASTMatchersMacros.h" |
8 | #include "clang/Basic/OperatorKinds.h" |
9 | |
10 | namespace clang::dataflow { |
11 | |
12 | namespace { |
13 | |
14 | using ast_matchers::callee; |
15 | using ast_matchers::cxxMemberCallExpr; |
16 | using ast_matchers::cxxMethodDecl; |
17 | using ast_matchers::cxxOperatorCallExpr; |
18 | using ast_matchers::hasCanonicalType; |
19 | using ast_matchers::hasName; |
20 | using ast_matchers::hasOverloadedOperatorName; |
21 | using ast_matchers::ofClass; |
22 | using ast_matchers::parameterCountIs; |
23 | using ast_matchers::pointerType; |
24 | using ast_matchers::referenceType; |
25 | using ast_matchers::returns; |
26 | |
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) { |
46 | // We may want to cache this search, but in current profiles it hasn't shown |
47 | // up as a hot spot (possibly because there aren't many hits, relatively). |
48 | CanQualType StarReturnType, ArrowReturnType; |
49 | for (const auto *MD : RD.methods()) { |
50 | // We only consider methods that are const and have zero parameters. |
51 | // It may be that there is a non-const overload for the method, but |
52 | // there should at least be a const overload as well. |
53 | if (!MD->isConst() || MD->getNumParams() != 0) |
54 | continue; |
55 | switch (MD->getOverloadedOperator()) { |
56 | case OO_Star: |
57 | StarReturnType = valueLikeReturnType(RT: MD->getReturnType()); |
58 | break; |
59 | case OO_Arrow: |
60 | ArrowReturnType = getLikeReturnType(RT: MD->getReturnType()); |
61 | break; |
62 | default: |
63 | break; |
64 | } |
65 | } |
66 | if (!StarReturnType.isNull() && !ArrowReturnType.isNull() && |
67 | StarReturnType == ArrowReturnType) |
68 | return StarReturnType; |
69 | |
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(Str: MethodName)) |
83 | return MD->getReturnType(); |
84 | } |
85 | return {}; |
86 | } |
87 | |
88 | } // namespace |
89 | } // namespace clang::dataflow |
90 | |
91 | // AST_MATCHER macros create an "internal" namespace, so we put it in |
92 | // its own anonymous namespace instead of in clang::dataflow. |
93 | namespace { |
94 | |
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(RD: Node); |
103 | if (RT.isNull()) |
104 | return false; |
105 | return getLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT; |
106 | } |
107 | |
108 | AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike, |
109 | clang::StringRef, MethodName) { |
110 | auto RT = pointerLikeReturnType(RD: Node); |
111 | if (RT.isNull()) |
112 | return false; |
113 | return valueLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT; |
114 | } |
115 | |
116 | AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) { |
117 | auto RT = pointerLikeReturnType(RD: Node); |
118 | if (RT.isNull()) |
119 | return false; |
120 | return getLikeReturnType(RT: findReturnType(RD: Node, MethodName: "get" )) == RT || |
121 | valueLikeReturnType(RT: findReturnType(RD: Node, MethodName: "value" )) == RT; |
122 | } |
123 | |
124 | AST_MATCHER(clang::CXXRecordDecl, pointerClass) { |
125 | return !pointerLikeReturnType(RD: Node).isNull(); |
126 | } |
127 | |
128 | } // namespace |
129 | |
130 | namespace clang::dataflow { |
131 | |
132 | ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() { |
133 | return cxxOperatorCallExpr( |
134 | hasOverloadedOperatorName(Name: "*" ), |
135 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
136 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
137 | ofClass(InnerMatcher: smartPointerClassWithGetOrValue())))); |
138 | } |
139 | |
140 | ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() { |
141 | return cxxOperatorCallExpr( |
142 | hasOverloadedOperatorName(Name: "->" ), |
143 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
144 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
145 | ofClass(InnerMatcher: smartPointerClassWithGetOrValue())))); |
146 | } |
147 | |
148 | ast_matchers::StatementMatcher isPointerLikeOperatorStar() { |
149 | return cxxOperatorCallExpr( |
150 | hasOverloadedOperatorName(Name: "*" ), |
151 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
152 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
153 | ofClass(InnerMatcher: pointerClass())))); |
154 | } |
155 | |
156 | ast_matchers::StatementMatcher isPointerLikeOperatorArrow() { |
157 | return cxxOperatorCallExpr( |
158 | hasOverloadedOperatorName(Name: "->" ), |
159 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
160 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
161 | ofClass(InnerMatcher: pointerClass())))); |
162 | } |
163 | |
164 | ast_matchers::StatementMatcher |
165 | isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) { |
166 | return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl( |
167 | parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
168 | hasName(Name: MethodName), |
169 | ofClass(InnerMatcher: smartPointerClassWithValueLike(MethodName))))); |
170 | } |
171 | |
172 | ast_matchers::StatementMatcher |
173 | isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) { |
174 | return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl( |
175 | parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
176 | hasName(Name: MethodName), ofClass(InnerMatcher: smartPointerClassWithGetLike(MethodName))))); |
177 | } |
178 | |
179 | const FunctionDecl * |
180 | getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) { |
181 | const FunctionDecl *CanonicalCallee = nullptr; |
182 | const CXXMethodDecl *Callee = |
183 | cast_or_null<CXXMethodDecl>(Val: CE->getDirectCallee()); |
184 | if (Callee == nullptr) |
185 | return nullptr; |
186 | const CXXRecordDecl *RD = Callee->getParent(); |
187 | if (RD == nullptr) |
188 | return nullptr; |
189 | for (const auto *MD : RD->methods()) { |
190 | if (MD->getOverloadedOperator() == OO_Star && MD->isConst() && |
191 | MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) { |
192 | CanonicalCallee = MD; |
193 | break; |
194 | } |
195 | } |
196 | return CanonicalCallee; |
197 | } |
198 | |
199 | } // namespace clang::dataflow |
200 | |