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"
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();
15 return std::vector
<const Stmt
*>();
18 // We build a Control Flow Graph (CFG) fron the body of the function
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
>();
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
) {
41 // We want our declaration to be used on the right hand side of the
43 auto DeclRef
= dyn_cast
<DeclRefExpr
>(IgnoreTrivials(BinOp
->getRHS()));
48 if (DeclRef
->getDecl() != ValueDeclaration
) {
51 } else if (auto Return
= dyn_cast
<ReturnStmt
>(CFGStatement
->getStmt())) {
52 // We want our declaration to be used as the expression of the return
54 auto DeclRef
= dyn_cast_or_null
<DeclRefExpr
>(
55 IgnoreTrivials(Return
->getRetValue()));
60 if (DeclRef
->getDecl() != ValueDeclaration
) {
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.
77 template <> struct is_error_code_enum
<EscapesFunctionError
> : true_type
{};
80 // We define the EscapesFunctionErrorCategory which contains the error messages
81 // corresponding to each enum variant.
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
{};
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();
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();
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();
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) {
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
) {
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
182 if (FuncDecl
->isVariadic()) {
183 return EscapesFunctionError::FunctionIsVariadic
;
186 // We find the argument number corresponding to the Arg expression.
188 for (unsigned i
= 0; i
< NumArgs
; i
++) {
189 if (IgnoreTrivials(Arg
) == IgnoreTrivials(Arguments
[i
])) {
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
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());
230 if (auto ParamDeclaration
= dyn_cast
<ParmVarDecl
>(DeclRef
->getDecl())) {
231 // This is the case where the parameter escapes through another
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) {
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()) {
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
249 if (!VarDeclaration
->hasGlobalStorage()) {
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
263 if (!FuncDecl
->getReturnType()->isPointerType() &&
264 !FuncDecl
->getReturnType()->isReferenceType()) {
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);