1//=======- ASTUtils.cpp ------------------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ASTUtils.h"
10#include "PtrTypesSemantics.h"
11#include "clang/AST/Attr.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/ExprCXX.h"
15#include "clang/AST/ExprObjC.h"
16#include "clang/AST/StmtVisitor.h"
17#include <optional>
18
19namespace clang {
20
21bool isSafePtr(clang::CXXRecordDecl *Decl) {
22 return isRefCounted(Class: Decl) || isCheckedPtr(Class: Decl);
23}
24
25bool tryToFindPtrOrigin(
26 const Expr *E, bool StopAtFirstRefCountedObj,
27 std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
28 std::function<bool(const clang::QualType)> isSafePtrType,
29 std::function<bool(const clang::Expr *, bool)> callback) {
30 while (E) {
31 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: E)) {
32 if (auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl())) {
33 auto QT = VD->getType();
34 if (VD->hasGlobalStorage() && QT.isConstQualified()) {
35 return callback(E, true);
36 }
37 }
38 }
39 if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(Val: E)) {
40 E = tempExpr->getSubExpr();
41 continue;
42 }
43 if (auto *tempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: E)) {
44 E = tempExpr->getSubExpr();
45 continue;
46 }
47 if (auto *tempExpr = dyn_cast<CXXConstructExpr>(Val: E)) {
48 if (auto *C = tempExpr->getConstructor()) {
49 if (auto *Class = C->getParent(); Class && isSafePtr(Class))
50 return callback(E, true);
51 break;
52 }
53 }
54 if (auto *TempExpr = dyn_cast<CXXUnresolvedConstructExpr>(Val: E)) {
55 if (isSafePtrType(TempExpr->getTypeAsWritten()))
56 return callback(TempExpr, true);
57 }
58 if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) {
59 if (auto *RF = POE->getResultExpr()) {
60 E = RF;
61 continue;
62 }
63 }
64 if (auto *tempExpr = dyn_cast<ParenExpr>(Val: E)) {
65 E = tempExpr->getSubExpr();
66 continue;
67 }
68 if (auto *OpaqueValue = dyn_cast<OpaqueValueExpr>(Val: E)) {
69 E = OpaqueValue->getSourceExpr();
70 continue;
71 }
72 if (auto *Expr = dyn_cast<ConditionalOperator>(Val: E)) {
73 return tryToFindPtrOrigin(E: Expr->getTrueExpr(), StopAtFirstRefCountedObj,
74 isSafePtr, isSafePtrType, callback) &&
75 tryToFindPtrOrigin(E: Expr->getFalseExpr(), StopAtFirstRefCountedObj,
76 isSafePtr, isSafePtrType, callback);
77 }
78 if (auto *cast = dyn_cast<CastExpr>(Val: E)) {
79 if (StopAtFirstRefCountedObj) {
80 if (auto *ConversionFunc =
81 dyn_cast_or_null<FunctionDecl>(Val: cast->getConversionFunction())) {
82 if (isCtorOfSafePtr(F: ConversionFunc))
83 return callback(E, true);
84 }
85 if (isa<CXXFunctionalCastExpr>(Val: E) && isSafePtrType(cast->getType()))
86 return callback(E, true);
87 }
88 // FIXME: This can give false "origin" that would lead to false negatives
89 // in checkers. See https://reviews.llvm.org/D37023 for reference.
90 E = cast->getSubExpr();
91 continue;
92 }
93 if (auto *call = dyn_cast<CallExpr>(Val: E)) {
94 if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(Val: call)) {
95 if (auto *decl = memberCall->getMethodDecl()) {
96 std::optional<bool> IsGetterOfRefCt = isGetterOfSafePtr(Method: decl);
97 if (IsGetterOfRefCt && *IsGetterOfRefCt) {
98 E = memberCall->getImplicitObjectArgument();
99 if (StopAtFirstRefCountedObj) {
100 return callback(E, true);
101 }
102 continue;
103 }
104 }
105 }
106
107 if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(Val: E)) {
108 if (auto *Callee = operatorCall->getDirectCallee()) {
109 auto ClsName = safeGetName(ASTNode: Callee->getParent());
110 if (isRefType(Name: ClsName) || isCheckedPtr(Name: ClsName) ||
111 isRetainPtr(Name: ClsName) || ClsName == "unique_ptr" ||
112 ClsName == "UniqueRef" || ClsName == "WeakPtr" ||
113 ClsName == "WeakRef") {
114 if (operatorCall->getNumArgs() == 1) {
115 E = operatorCall->getArg(Arg: 0);
116 continue;
117 }
118 }
119 }
120 }
121
122 if (call->isCallToStdMove() && call->getNumArgs() == 1) {
123 E = call->getArg(Arg: 0)->IgnoreParenCasts();
124 continue;
125 }
126
127 if (auto *callee = call->getDirectCallee()) {
128 if (isCtorOfSafePtr(F: callee)) {
129 if (StopAtFirstRefCountedObj)
130 return callback(E, true);
131
132 E = call->getArg(Arg: 0);
133 continue;
134 }
135
136 if (isSafePtrType(callee->getReturnType()))
137 return callback(E, true);
138
139 if (isSingleton(F: callee))
140 return callback(E, true);
141
142 if (callee->isInStdNamespace() && safeGetName(ASTNode: callee) == "forward") {
143 E = call->getArg(Arg: 0);
144 continue;
145 }
146
147 if (isPtrConversion(F: callee)) {
148 E = call->getArg(Arg: 0);
149 continue;
150 }
151
152 auto Name = safeGetName(ASTNode: callee);
153 if (Name == "__builtin___CFStringMakeConstantString" ||
154 Name == "NSClassFromString")
155 return callback(E, true);
156 }
157 }
158 if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E)) {
159 if (auto *Method = ObjCMsgExpr->getMethodDecl()) {
160 if (isSafePtrType(Method->getReturnType()))
161 return callback(E, true);
162 }
163 auto Selector = ObjCMsgExpr->getSelector();
164 auto NameForFirstSlot = Selector.getNameForSlot(argIndex: 0);
165 if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass") &&
166 !Selector.getNumArgs())
167 return callback(E, true);
168 }
169 if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(Val: E))
170 return callback(ObjCDict, true);
171 if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(Val: E))
172 return callback(ObjCArray, true);
173 if (auto *ObjCStr = dyn_cast<ObjCStringLiteral>(Val: E))
174 return callback(ObjCStr, true);
175 if (auto *unaryOp = dyn_cast<UnaryOperator>(Val: E)) {
176 // FIXME: Currently accepts ANY unary operator. Is it OK?
177 E = unaryOp->getSubExpr();
178 continue;
179 }
180
181 break;
182 }
183 // Some other expression.
184 return callback(E, false);
185}
186
187bool isASafeCallArg(const Expr *E) {
188 assert(E);
189 if (auto *Ref = dyn_cast<DeclRefExpr>(Val: E)) {
190 auto *FoundDecl = Ref->getFoundDecl();
191 if (auto *D = dyn_cast_or_null<VarDecl>(Val: FoundDecl)) {
192 if (isa<ParmVarDecl>(Val: D) || D->isLocalVarDecl())
193 return true;
194 if (auto *ImplicitP = dyn_cast<ImplicitParamDecl>(Val: D)) {
195 auto Kind = ImplicitP->getParameterKind();
196 if (Kind == ImplicitParamKind::ObjCSelf ||
197 Kind == ImplicitParamKind::ObjCCmd ||
198 Kind == ImplicitParamKind::CXXThis ||
199 Kind == ImplicitParamKind::CXXVTT)
200 return true;
201 }
202 } else if (auto *BD = dyn_cast_or_null<BindingDecl>(Val: FoundDecl)) {
203 VarDecl *VD = BD->getHoldingVar();
204 if (VD && (isa<ParmVarDecl>(Val: VD) || VD->isLocalVarDecl()))
205 return true;
206 }
207 }
208 if (isConstOwnerPtrMemberExpr(E))
209 return true;
210
211 // TODO: checker for method calls on non-refcounted objects
212 return isa<CXXThisExpr>(Val: E);
213}
214
215bool isConstOwnerPtrMemberExpr(const clang::Expr *E) {
216 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: E)) {
217 if (auto *Callee = MCE->getDirectCallee()) {
218 auto Name = safeGetName(ASTNode: Callee);
219 if (Name == "get" || Name == "ptr")
220 E = MCE->getImplicitObjectArgument();
221 if (isa<CXXConversionDecl>(Val: Callee))
222 E = MCE->getImplicitObjectArgument();
223 }
224 } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(Val: E)) {
225 if (OCE->getOperator() == OO_Star && OCE->getNumArgs() == 1)
226 E = OCE->getArg(Arg: 0);
227 }
228 const ValueDecl *D = nullptr;
229 if (auto *ME = dyn_cast<MemberExpr>(Val: E))
230 D = ME->getMemberDecl();
231 else if (auto *IVR = dyn_cast<ObjCIvarRefExpr>(Val: E))
232 D = IVR->getDecl();
233 if (!D)
234 return false;
235 auto T = D->getType();
236 return isOwnerPtrType(T) && T.isConstQualified();
237}
238
239bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E) {
240 auto *ME = dyn_cast<MemberExpr>(Val: E);
241 if (!ME)
242 return false;
243 auto *Base = ME->getBase();
244 if (!Base)
245 return false;
246 if (!isa<CXXThisExpr>(Val: Base->IgnoreParenCasts()))
247 return false;
248 auto *D = ME->getMemberDecl();
249 if (!D)
250 return false;
251 auto T = D->getType();
252 auto *CXXRD = T->getAsCXXRecordDecl();
253 if (!CXXRD)
254 return false;
255 auto result = isCheckedPtrCapable(Class: CXXRD);
256 return result && *result;
257}
258
259class EnsureFunctionVisitor
260 : public ConstStmtVisitor<EnsureFunctionVisitor, bool> {
261public:
262 bool VisitStmt(const Stmt *S) {
263 for (const Stmt *Child : S->children()) {
264 if (Child && !Visit(S: Child))
265 return false;
266 }
267 return true;
268 }
269
270 bool VisitReturnStmt(const ReturnStmt *RS) {
271 if (auto *RV = RS->getRetValue()) {
272 RV = RV->IgnoreParenCasts();
273 if (isa<CXXNullPtrLiteralExpr>(Val: RV))
274 return true;
275 return isConstOwnerPtrMemberExpr(E: RV);
276 }
277 return false;
278 }
279};
280
281bool EnsureFunctionAnalysis::isACallToEnsureFn(const clang::Expr *E) const {
282 auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: E);
283 if (!MCE)
284 return false;
285 auto *Callee = MCE->getDirectCallee();
286 if (!Callee)
287 return false;
288 auto *Body = Callee->getBody();
289 if (!Body || Callee->isVirtualAsWritten())
290 return false;
291 auto [CacheIt, IsNew] = Cache.insert(KV: std::make_pair(x&: Callee, y: false));
292 if (IsNew)
293 CacheIt->second = EnsureFunctionVisitor().Visit(S: Body);
294 return CacheIt->second;
295}
296
297} // namespace clang
298