Bug 1814798 - pt 1. Add bool to enable/disable PHC at runtime r=glandium
[gecko.git] / build / clang-plugin / VariableUsageHelpers.cpp
blobabb9eb280fe290c28115caa34bbe193e09852c80
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "VariableUsageHelpers.h"
6 #include "Utils.h"
8 std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration,
9 const FunctionDecl *FuncDecl) {
10 std::vector<const Stmt *> UsageStatements;
12 // We check the function declaration has a body.
13 auto Body = FuncDecl->getBody();
14 if (!Body) {
15 return std::vector<const Stmt *>();
18 // We build a Control Flow Graph (CFG) fron the body of the function
19 // declaration.
20 std::unique_ptr<CFG> StatementCFG = CFG::buildCFG(
21 FuncDecl, Body, &FuncDecl->getASTContext(), CFG::BuildOptions());
23 // We iterate through all the CFGBlocks, which basically means that we go over
24 // all the possible branches of the code and therefore cover all statements.
25 for (auto &Block : *StatementCFG) {
26 // We iterate through all the statements of the block.
27 for (auto &BlockItem : *Block) {
28 auto CFGStatement = BlockItem.getAs<CFGStmt>();
29 if (!CFGStatement) {
30 continue;
33 // FIXME: Right now this function/if chain is very basic and only covers
34 // the cases we need for escapesFunction()
35 if (auto BinOp = dyn_cast<BinaryOperator>(CFGStatement->getStmt())) {
36 // We only care about assignments.
37 if (BinOp->getOpcode() != BO_Assign) {
38 continue;
41 // We want our declaration to be used on the right hand side of the
42 // assignment.
43 auto DeclRef = dyn_cast<DeclRefExpr>(IgnoreTrivials(BinOp->getRHS()));
44 if (!DeclRef) {
45 continue;
48 if (DeclRef->getDecl() != ValueDeclaration) {
49 continue;
51 } else if (auto Return = dyn_cast<ReturnStmt>(CFGStatement->getStmt())) {
52 // We want our declaration to be used as the expression of the return
53 // statement.
54 auto DeclRef = dyn_cast_or_null<DeclRefExpr>(
55 IgnoreTrivials(Return->getRetValue()));
56 if (!DeclRef) {
57 continue;
60 if (DeclRef->getDecl() != ValueDeclaration) {
61 continue;
63 } else {
64 continue;
67 // We didn't early-continue, so we add the statement to the list.
68 UsageStatements.push_back(CFGStatement->getStmt());
72 return UsageStatements;
75 // We declare our EscapesFunctionError enum to be an error code enum.
76 namespace std {
77 template <> struct is_error_code_enum<EscapesFunctionError> : true_type {};
78 } // namespace std
80 // We define the EscapesFunctionErrorCategory which contains the error messages
81 // corresponding to each enum variant.
82 namespace {
83 struct EscapesFunctionErrorCategory : std::error_category {
84 const char *name() const noexcept override;
85 std::string message(int ev) const override;
88 const char *EscapesFunctionErrorCategory::name() const noexcept {
89 return "escapes function";
92 std::string EscapesFunctionErrorCategory::message(int ev) const {
93 switch (static_cast<EscapesFunctionError>(ev)) {
94 case EscapesFunctionError::ConstructorDeclNotFound:
95 return "constructor declaration not found";
97 case EscapesFunctionError::FunctionDeclNotFound:
98 return "function declaration not found";
100 case EscapesFunctionError::FunctionIsBuiltin:
101 return "function is builtin";
103 case EscapesFunctionError::FunctionIsVariadic:
104 return "function is variadic";
106 case EscapesFunctionError::ExprNotInCall:
107 return "expression is not in call";
109 case EscapesFunctionError::NoParamForArg:
110 return "no parameter for argument";
112 case EscapesFunctionError::ArgAndParamNotPointers:
113 return "argument and parameter are not pointers";
117 const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{};
118 } // namespace
120 std::error_code make_error_code(EscapesFunctionError e) {
121 return {static_cast<int>(e), TheEscapesFunctionErrorCategory};
124 ErrorOr<std::tuple<const Stmt *, const Decl *>>
125 escapesFunction(const Expr *Arg, const CXXConstructExpr *Construct) {
126 // We get the function declaration corresponding to the call.
127 auto CtorDecl = Construct->getConstructor();
128 if (!CtorDecl) {
129 return EscapesFunctionError::ConstructorDeclNotFound;
132 return escapesFunction(Arg, CtorDecl, Construct->getArgs(),
133 Construct->getNumArgs());
136 ErrorOr<std::tuple<const Stmt *, const Decl *>>
137 escapesFunction(const Expr *Arg, const CallExpr *Call) {
138 // We get the function declaration corresponding to the call.
139 auto FuncDecl = Call->getDirectCallee();
140 if (!FuncDecl) {
141 return EscapesFunctionError::FunctionDeclNotFound;
144 return escapesFunction(Arg, FuncDecl, Call->getArgs(), Call->getNumArgs());
147 ErrorOr<std::tuple<const Stmt *, const Decl *>>
148 escapesFunction(const Expr *Arg, const CXXOperatorCallExpr *OpCall) {
149 // We get the function declaration corresponding to the operator call.
150 auto FuncDecl = OpCall->getDirectCallee();
151 if (!FuncDecl) {
152 return EscapesFunctionError::FunctionDeclNotFound;
155 auto Args = OpCall->getArgs();
156 auto NumArgs = OpCall->getNumArgs();
157 // If this is an infix binary operator defined as a one-param method, we
158 // remove the first argument as it is inserted explicitly and creates a
159 // mismatch with the parameters of the method declaration.
160 if (isInfixBinaryOp(OpCall) && FuncDecl->getNumParams() == 1) {
161 Args++;
162 NumArgs--;
165 return escapesFunction(Arg, FuncDecl, Args, NumArgs);
168 ErrorOr<std::tuple<const Stmt *, const Decl *>>
169 escapesFunction(const Expr *Arg, const FunctionDecl *FuncDecl,
170 const Expr *const *Arguments, unsigned NumArgs) {
171 if (!NumArgs) {
172 return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr);
175 if (FuncDecl->getBuiltinID() != 0 ||
176 ASTIsInSystemHeader(FuncDecl->getASTContext(), *FuncDecl)) {
177 return EscapesFunctionError::FunctionIsBuiltin;
180 // FIXME: should probably be handled at some point, but it's too annoying
181 // for now.
182 if (FuncDecl->isVariadic()) {
183 return EscapesFunctionError::FunctionIsVariadic;
186 // We find the argument number corresponding to the Arg expression.
187 unsigned ArgNum = 0;
188 for (unsigned i = 0; i < NumArgs; i++) {
189 if (IgnoreTrivials(Arg) == IgnoreTrivials(Arguments[i])) {
190 break;
192 ++ArgNum;
194 // If we don't find it, we early-return NoneType.
195 if (ArgNum >= NumArgs) {
196 return EscapesFunctionError::ExprNotInCall;
199 // Now we get the associated parameter.
200 if (ArgNum >= FuncDecl->getNumParams()) {
201 return EscapesFunctionError::NoParamForArg;
203 auto Param = FuncDecl->getParamDecl(ArgNum);
205 // We want both the argument and the parameter to be of pointer type.
206 // FIXME: this is enough for the DanglingOnTemporaryChecker, because the
207 // analysed methods only return pointers, but more cases should probably be
208 // handled when we want to use this function more broadly.
209 if ((!Arg->getType().getNonReferenceType()->isPointerType() &&
210 Arg->getType().getNonReferenceType()->isBuiltinType()) ||
211 (!Param->getType().getNonReferenceType()->isPointerType() &&
212 Param->getType().getNonReferenceType()->isBuiltinType())) {
213 return EscapesFunctionError::ArgAndParamNotPointers;
216 // We retrieve the usages of the parameter in the function.
217 auto Usages = getUsageAsRvalue(Param, FuncDecl);
219 // For each usage, we check if it doesn't allow the parameter to escape the
220 // function scope.
221 for (auto Usage : Usages) {
222 // In the case of an assignment.
223 if (auto BinOp = dyn_cast<BinaryOperator>(Usage)) {
224 // We retrieve the declaration the parameter is assigned to.
225 auto DeclRef = dyn_cast<DeclRefExpr>(BinOp->getLHS());
226 if (!DeclRef) {
227 continue;
230 if (auto ParamDeclaration = dyn_cast<ParmVarDecl>(DeclRef->getDecl())) {
231 // This is the case where the parameter escapes through another
232 // parameter.
234 // FIXME: for now we only care about references because we only detect
235 // trivial LHS with just a DeclRefExpr, and not more complex cases like:
236 // void func(Type* param1, Type** param2) {
237 // *param2 = param1;
238 // }
239 // This should be fixed when we have better/more helper functions to
240 // help deal with this kind of lvalue expressions.
241 if (!ParamDeclaration->getType()->isReferenceType()) {
242 continue;
245 return std::make_tuple(Usage, (const Decl *)ParamDeclaration);
246 } else if (auto VarDeclaration = dyn_cast<VarDecl>(DeclRef->getDecl())) {
247 // This is the case where the parameter escapes through a global/static
248 // variable.
249 if (!VarDeclaration->hasGlobalStorage()) {
250 continue;
253 return std::make_tuple(Usage, (const Decl *)VarDeclaration);
254 } else if (auto FieldDeclaration =
255 dyn_cast<FieldDecl>(DeclRef->getDecl())) {
256 // This is the case where the parameter escapes through a field.
258 return std::make_tuple(Usage, (const Decl *)FieldDeclaration);
260 } else if (isa<ReturnStmt>(Usage)) {
261 // This is the case where the parameter escapes through the return value
262 // of the function.
263 if (!FuncDecl->getReturnType()->isPointerType() &&
264 !FuncDecl->getReturnType()->isReferenceType()) {
265 continue;
268 return std::make_tuple(Usage, (const Decl *)FuncDecl);
272 // No early-return, this means that we haven't found any case of funciton
273 // escaping and that therefore the parameter remains in the function scope.
274 return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr);