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