1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
//===-- ASTOps.cc -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Operations on AST nodes that are used in flow-sensitive analysis.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/FlowSensitive/ASTOps.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ComputeDependence.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include <cassert>
#include <iterator>
#include <vector>
#define DEBUG_TYPE "dataflow"
namespace clang::dataflow {
const Expr &ignoreCFGOmittedNodes(const Expr &E) {
const Expr *Current = &E;
const Expr *Last = nullptr;
while (Current != Last) {
Last = Current;
if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
Current = EWC->getSubExpr();
assert(Current != nullptr);
}
if (auto *CE = dyn_cast<ConstantExpr>(Current)) {
Current = CE->getSubExpr();
assert(Current != nullptr);
}
Current = Current->IgnoreParens();
assert(Current != nullptr);
}
return *Current;
}
const Stmt &ignoreCFGOmittedNodes(const Stmt &S) {
if (auto *E = dyn_cast<Expr>(&S))
return ignoreCFGOmittedNodes(*E);
return S;
}
// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
// field decl will be modeled for all instances of the inherited field.
static void getFieldsFromClassHierarchy(QualType Type, FieldSet &Fields) {
if (Type->isIncompleteType() || Type->isDependentType() ||
!Type->isRecordType())
return;
Fields.insert_range(Type->getAsRecordDecl()->fields());
if (auto *CXXRecord = Type->getAsCXXRecordDecl())
for (const CXXBaseSpecifier &Base : CXXRecord->bases())
getFieldsFromClassHierarchy(Base.getType(), Fields);
}
/// Gets the set of all fields in the type.
FieldSet getObjectFields(QualType Type) {
FieldSet Fields;
getFieldsFromClassHierarchy(Type, Fields);
return Fields;
}
bool containsSameFields(const FieldSet &Fields,
const RecordStorageLocation::FieldToLoc &FieldLocs) {
if (Fields.size() != FieldLocs.size())
return false;
for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)
if (!Fields.contains(cast_or_null<FieldDecl>(Field)))
return false;
return true;
}
/// Returns the fields of a `RecordDecl` that are initialized by an
/// `InitListExpr` or `CXXParenListInitExpr`, in the order in which they appear
/// in `InitListExpr::inits()` / `CXXParenListInitExpr::getInitExprs()`.
/// `InitList->getType()` must be a record type.
template <class InitListT>
static std::vector<const FieldDecl *>
getFieldsForInitListExpr(const InitListT *InitList) {
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
assert(RD != nullptr);
std::vector<const FieldDecl *> Fields;
if (InitList->getType()->isUnionType()) {
if (const FieldDecl *Field = InitList->getInitializedFieldInUnion())
Fields.push_back(Field);
return Fields;
}
// Unnamed bitfields are only used for padding and do not appear in
// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
// field list, and we thus need to remove them before mapping inits to
// fields to avoid mapping inits to the wrongs fields.
llvm::copy_if(
RD->fields(), std::back_inserter(Fields),
[](const FieldDecl *Field) { return !Field->isUnnamedBitField(); });
return Fields;
}
RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList)
: RecordInitListHelper(InitList->getType(),
getFieldsForInitListExpr(InitList),
InitList->inits()) {}
RecordInitListHelper::RecordInitListHelper(
const CXXParenListInitExpr *ParenInitList)
: RecordInitListHelper(ParenInitList->getType(),
getFieldsForInitListExpr(ParenInitList),
ParenInitList->getInitExprs()) {}
RecordInitListHelper::RecordInitListHelper(
QualType Ty, std::vector<const FieldDecl *> Fields,
ArrayRef<Expr *> Inits) {
auto *RD = Ty->getAsCXXRecordDecl();
assert(RD != nullptr);
// Unions initialized with an empty initializer list need special treatment.
// For structs/classes initialized with an empty initializer list, Clang
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
SmallVector<Expr *> InitsForUnion;
if (Ty->isUnionType() && Inits.empty()) {
assert(Fields.size() <= 1);
if (!Fields.empty()) {
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
}
Inits = InitsForUnion;
}
size_t InitIdx = 0;
assert(Fields.size() + RD->getNumBases() == Inits.size());
for (const CXXBaseSpecifier &Base : RD->bases()) {
assert(InitIdx < Inits.size());
Expr *Init = Inits[InitIdx++];
BaseInits.emplace_back(&Base, Init);
}
assert(Fields.size() == Inits.size() - InitIdx);
for (const FieldDecl *Field : Fields) {
assert(InitIdx < Inits.size());
Expr *Init = Inits[InitIdx++];
FieldInits.emplace_back(Field, Init);
}
}
static void insertIfGlobal(const Decl &D,
llvm::DenseSet<const VarDecl *> &Globals) {
if (auto *V = dyn_cast<VarDecl>(&D))
if (V->hasGlobalStorage())
Globals.insert(V);
}
static void insertIfLocal(const Decl &D,
llvm::DenseSet<const VarDecl *> &Locals) {
if (auto *V = dyn_cast<VarDecl>(&D))
if (V->hasLocalStorage() && !isa<ParmVarDecl>(V))
Locals.insert(V);
}
static void insertIfFunction(const Decl &D,
llvm::DenseSet<const FunctionDecl *> &Funcs) {
if (auto *FD = dyn_cast<FunctionDecl>(&D))
Funcs.insert(FD);
}
static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
// Use getCalleeDecl instead of getMethodDecl in order to handle
// pointer-to-member calls.
const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());
if (!MethodDecl)
return nullptr;
auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());
if (!Body || Body->size() != 1)
return nullptr;
if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))
if (auto *Return = RS->getRetValue())
return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts());
return nullptr;
}
class ReferencedDeclsVisitor : public AnalysisASTVisitor {
public:
ReferencedDeclsVisitor(ReferencedDecls &Referenced)
: Referenced(Referenced) {}
void traverseConstructorInits(const CXXConstructorDecl *Ctor) {
for (const CXXCtorInitializer *Init : Ctor->inits()) {
if (Init->isMemberInitializer()) {
Referenced.Fields.insert(Init->getMember());
} else if (Init->isIndirectMemberInitializer()) {
for (const auto *I : Init->getIndirectMember()->chain())
Referenced.Fields.insert(cast<FieldDecl>(I));
}
Expr *InitExpr = Init->getInit();
// Also collect declarations referenced in `InitExpr`.
TraverseStmt(InitExpr);
// If this is a `CXXDefaultInitExpr`, also collect declarations referenced
// within the default expression.
if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))
TraverseStmt(DefaultInit->getExpr());
}
}
bool VisitDecl(Decl *D) override {
insertIfGlobal(*D, Referenced.Globals);
insertIfLocal(*D, Referenced.Locals);
insertIfFunction(*D, Referenced.Functions);
return true;
}
bool VisitDeclRefExpr(DeclRefExpr *E) override {
insertIfGlobal(*E->getDecl(), Referenced.Globals);
insertIfLocal(*E->getDecl(), Referenced.Locals);
insertIfFunction(*E->getDecl(), Referenced.Functions);
return true;
}
bool VisitCXXMemberCallExpr(CXXMemberCallExpr *C) override {
// If this is a method that returns a member variable but does nothing else,
// model the field of the return value.
if (MemberExpr *E = getMemberForAccessor(*C))
if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))
Referenced.Fields.insert(FD);
return true;
}
bool VisitMemberExpr(MemberExpr *E) override {
// FIXME: should we be using `E->getFoundDecl()`?
const ValueDecl *VD = E->getMemberDecl();
insertIfGlobal(*VD, Referenced.Globals);
insertIfFunction(*VD, Referenced.Functions);
if (const auto *FD = dyn_cast<FieldDecl>(VD))
Referenced.Fields.insert(FD);
return true;
}
bool VisitInitListExpr(InitListExpr *InitList) override {
if (InitList->getType()->isRecordType())
Referenced.Fields.insert_range(getFieldsForInitListExpr(InitList));
return true;
}
bool VisitCXXParenListInitExpr(CXXParenListInitExpr *ParenInitList) override {
if (ParenInitList->getType()->isRecordType())
Referenced.Fields.insert_range(getFieldsForInitListExpr(ParenInitList));
return true;
}
private:
ReferencedDecls &Referenced;
};
ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
ReferencedDecls Result;
ReferencedDeclsVisitor Visitor(Result);
Visitor.TraverseStmt(FD.getBody());
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
Visitor.traverseConstructorInits(CtorDecl);
// If analyzing a lambda call operator, collect all captures of parameters (of
// the surrounding function). This collects them even if they are not
// referenced in the body of the lambda call operator. Non-parameter local
// variables that are captured are already collected into
// `ReferencedDecls.Locals` when traversing the call operator body, but we
// collect parameters here to avoid needing to check at each referencing node
// whether the parameter is a lambda capture from a surrounding function or is
// a parameter of the current function. If it becomes necessary to limit this
// set to the parameters actually referenced in the body, alternative
// optimizations can be implemented to minimize duplicative work.
if (const auto *Method = dyn_cast<CXXMethodDecl>(&FD);
Method && isLambdaCallOperator(Method)) {
for (const auto &Capture : Method->getParent()->captures()) {
if (Capture.capturesVariable()) {
if (const auto *Param =
dyn_cast<ParmVarDecl>(Capture.getCapturedVar())) {
Result.LambdaCapturedParams.insert(Param);
}
}
}
}
return Result;
}
ReferencedDecls getReferencedDecls(const Stmt &S) {
ReferencedDecls Result;
ReferencedDeclsVisitor Visitor(Result);
Visitor.TraverseStmt(const_cast<Stmt *>(&S));
return Result;
}
} // namespace clang::dataflow
|