Source code

Revision control

Other Tools

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/. */
4
5
#include "VariableUsageHelpers.h"
6
#include "Utils.h"
7
8
std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration,
9
const FunctionDecl *FuncDecl) {
10
std::vector<const Stmt *> UsageStatements;
11
12
// We check the function declaration has a body.
13
auto Body = FuncDecl->getBody();
14
if (!Body) {
15
return std::vector<const Stmt *>();
16
}
17
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());
22
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
Optional<CFGStmt> CFGStatement = BlockItem.getAs<CFGStmt>();
29
if (!CFGStatement) {
30
continue;
31
}
32
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;
39
}
40
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;
46
}
47
48
if (DeclRef->getDecl() != ValueDeclaration) {
49
continue;
50
}
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;
58
}
59
60
if (DeclRef->getDecl() != ValueDeclaration) {
61
continue;
62
}
63
} else {
64
continue;
65
}
66
67
// We didn't early-continue, so we add the statement to the list.
68
UsageStatements.push_back(CFGStatement->getStmt());
69
}
70
}
71
72
return UsageStatements;
73
}
74
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
79
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;
86
};
87
88
const char *EscapesFunctionErrorCategory::name() const noexcept {
89
return "escapes function";
90
}
91
92
std::string EscapesFunctionErrorCategory::message(int ev) const {
93
switch (static_cast<EscapesFunctionError>(ev)) {
94
case EscapesFunctionError::ConstructorDeclNotFound:
95
return "constructor declaration not found";
96
97
case EscapesFunctionError::FunctionDeclNotFound:
98
return "function declaration not found";
99
100
case EscapesFunctionError::FunctionIsBuiltin:
101
return "function is builtin";
102
103
case EscapesFunctionError::FunctionIsVariadic:
104
return "function is variadic";
105
106
case EscapesFunctionError::ExprNotInCall:
107
return "expression is not in call";
108
109
case EscapesFunctionError::NoParamForArg:
110
return "no parameter for argument";
111
112
case EscapesFunctionError::ArgAndParamNotPointers:
113
return "argument and parameter are not pointers";
114
}
115
}
116
117
const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{};
118
} // namespace
119
120
std::error_code make_error_code(EscapesFunctionError e) {
121
return {static_cast<int>(e), TheEscapesFunctionErrorCategory};
122
}
123
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;
130
}
131
132
return escapesFunction(Arg, CtorDecl, Construct->getArgs(),
133
Construct->getNumArgs());
134
}
135
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;
142
}
143
144
return escapesFunction(Arg, FuncDecl, Call->getArgs(), Call->getNumArgs());
145
}
146
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;
153
}
154
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--;
163
}
164
165
return escapesFunction(Arg, FuncDecl, Args, NumArgs);
166
}
167
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);
173
}
174
175
if (FuncDecl->getBuiltinID() != 0 ||
176
ASTIsInSystemHeader(FuncDecl->getASTContext(), *FuncDecl)) {
177
return EscapesFunctionError::FunctionIsBuiltin;
178
}
179
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;
184
}
185
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;
191
}
192
++ArgNum;
193
}
194
// If we don't find it, we early-return NoneType.
195
if (ArgNum >= NumArgs) {
196
return EscapesFunctionError::ExprNotInCall;
197
}
198
199
// Now we get the associated parameter.
200
if (ArgNum >= FuncDecl->getNumParams()) {
201
return EscapesFunctionError::NoParamForArg;
202
}
203
auto Param = FuncDecl->getParamDecl(ArgNum);
204
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;
214
}
215
216
// We retrieve the usages of the parameter in the function.
217
auto Usages = getUsageAsRvalue(Param, FuncDecl);
218
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;
228
}
229
230
if (auto ParamDeclaration = dyn_cast<ParmVarDecl>(DeclRef->getDecl())) {
231
// This is the case where the parameter escapes through another
232
// parameter.
233
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;
243
}
244
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;
251
}
252
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.
257
258
return std::make_tuple(Usage, (const Decl *)FieldDeclaration);
259
}
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;
266
}
267
268
return std::make_tuple(Usage, (const Decl *)FuncDecl);
269
}
270
}
271
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);
275
}