xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp (revision 5c20891b2bb60f82dd82a8e90b111f8c13a13ad4)
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/CXXInheritance.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 <optional>
17 
18 namespace clang {
19 
20 bool isSafePtr(clang::CXXRecordDecl *Decl) {
21   return isRefCounted(Decl) || isCheckedPtr(Decl);
22 }
23 
24 bool tryToFindPtrOrigin(
25     const Expr *E, bool StopAtFirstRefCountedObj,
26     std::function<bool(const clang::Expr *, bool)> callback) {
27   while (E) {
28     if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
29       E = tempExpr->getSubExpr();
30       continue;
31     }
32     if (auto *tempExpr = dyn_cast<CXXBindTemporaryExpr>(E)) {
33       E = tempExpr->getSubExpr();
34       continue;
35     }
36     if (auto *tempExpr = dyn_cast<CXXTemporaryObjectExpr>(E)) {
37       if (auto *C = tempExpr->getConstructor()) {
38         if (auto *Class = C->getParent(); Class && isSafePtr(Class))
39           return callback(E, true);
40         break;
41       }
42     }
43     if (auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
44       if (auto *RF = POE->getResultExpr()) {
45         E = RF;
46         continue;
47       }
48     }
49     if (auto *tempExpr = dyn_cast<ParenExpr>(E)) {
50       E = tempExpr->getSubExpr();
51       continue;
52     }
53     if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
54       return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
55                                 callback) &&
56              tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
57                                 callback);
58     }
59     if (auto *cast = dyn_cast<CastExpr>(E)) {
60       if (StopAtFirstRefCountedObj) {
61         if (auto *ConversionFunc =
62                 dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {
63           if (isCtorOfSafePtr(ConversionFunc))
64             return callback(E, true);
65         }
66       }
67       // FIXME: This can give false "origin" that would lead to false negatives
68       // in checkers. See https://reviews.llvm.org/D37023 for reference.
69       E = cast->getSubExpr();
70       continue;
71     }
72     if (auto *call = dyn_cast<CallExpr>(E)) {
73       if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
74         if (auto *decl = memberCall->getMethodDecl()) {
75           std::optional<bool> IsGetterOfRefCt = isGetterOfSafePtr(decl);
76           if (IsGetterOfRefCt && *IsGetterOfRefCt) {
77             E = memberCall->getImplicitObjectArgument();
78             if (StopAtFirstRefCountedObj) {
79               return callback(E, true);
80             }
81             continue;
82           }
83         }
84       }
85 
86       if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(E)) {
87         if (operatorCall->getNumArgs() == 1) {
88           E = operatorCall->getArg(0);
89           continue;
90         }
91       }
92 
93       if (auto *callee = call->getDirectCallee()) {
94         if (isCtorOfRefCounted(callee) || isCtorOfCheckedPtr(callee)) {
95           if (StopAtFirstRefCountedObj)
96             return callback(E, true);
97 
98           E = call->getArg(0);
99           continue;
100         }
101 
102         if (isSafePtrType(callee->getReturnType()))
103           return callback(E, true);
104 
105         if (isSingleton(callee))
106           return callback(E, true);
107 
108         if (callee->isInStdNamespace() && safeGetName(callee) == "forward") {
109           E = call->getArg(0);
110           continue;
111         }
112 
113         if (isPtrConversion(callee)) {
114           E = call->getArg(0);
115           continue;
116         }
117       }
118     }
119     if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
120       if (auto *Method = ObjCMsgExpr->getMethodDecl()) {
121         if (isSafePtrType(Method->getReturnType()))
122           return callback(E, true);
123       }
124     }
125     if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
126       // FIXME: Currently accepts ANY unary operator. Is it OK?
127       E = unaryOp->getSubExpr();
128       continue;
129     }
130 
131     break;
132   }
133   // Some other expression.
134   return callback(E, false);
135 }
136 
137 bool isASafeCallArg(const Expr *E) {
138   assert(E);
139   if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
140     if (auto *D = dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
141       if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())
142         return true;
143     }
144   }
145 
146   // TODO: checker for method calls on non-refcounted objects
147   return isa<CXXThisExpr>(E);
148 }
149 
150 } // namespace clang
151