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
#ifndef Utils_h__
6
#define Utils_h__
7
8
#include "CustomAttributes.h"
9
#include "ThirdPartyPaths.h"
10
#include "plugin.h"
11
12
inline StringRef getFilename(const SourceManager &SM, SourceLocation Loc) {
13
// We use the presumed location to handle #line directives and such, so the
14
// plugin is friendly to icecc / sccache users.
15
auto PL = SM.getPresumedLoc(Loc);
16
if (PL.isValid()) {
17
return StringRef(PL.getFilename());
18
}
19
return SM.getFilename(Loc);
20
}
21
22
// Check if the given expression contains an assignment expression.
23
// This can either take the form of a Binary Operator or a
24
// Overloaded Operator Call.
25
inline bool hasSideEffectAssignment(const Expr *Expression) {
26
if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
27
auto BinOp = OpCallExpr->getOperator();
28
if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) {
29
return true;
30
}
31
} else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) {
32
if (BinOpExpr->isAssignmentOp()) {
33
return true;
34
}
35
}
36
37
// Recurse to children.
38
for (const Stmt *SubStmt : Expression->children()) {
39
auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
40
if (ChildExpr && hasSideEffectAssignment(ChildExpr)) {
41
return true;
42
}
43
}
44
45
return false;
46
}
47
48
template <class T>
49
inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
50
auto &SourceManager = AC.getSourceManager();
51
auto ExpansionLoc = SourceManager.getExpansionLoc(D.getBeginLoc());
52
if (ExpansionLoc.isInvalid()) {
53
return false;
54
}
55
return SourceManager.isInSystemHeader(ExpansionLoc);
56
}
57
58
template <typename T> inline StringRef getNameChecked(const T &D) {
59
return D->getIdentifier() ? D->getName() : "";
60
}
61
62
/// A cached data of whether classes are refcounted or not.
63
typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>>
64
RefCountedMap;
65
extern RefCountedMap RefCountedClasses;
66
67
inline bool classHasAddRefRelease(const CXXRecordDecl *D) {
68
const RefCountedMap::iterator &It = RefCountedClasses.find(D);
69
if (It != RefCountedClasses.end()) {
70
return It->second.second;
71
}
72
73
bool SeenAddRef = false;
74
bool SeenRelease = false;
75
for (CXXRecordDecl::method_iterator Method = D->method_begin();
76
Method != D->method_end(); ++Method) {
77
const auto &Name = getNameChecked(Method);
78
if (Name == "AddRef") {
79
SeenAddRef = true;
80
} else if (Name == "Release") {
81
SeenRelease = true;
82
}
83
}
84
RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease);
85
return SeenAddRef && SeenRelease;
86
}
87
88
inline bool isClassRefCounted(QualType T);
89
90
inline bool isClassRefCounted(const CXXRecordDecl *D) {
91
// Normalize so that D points to the definition if it exists.
92
if (!D->hasDefinition())
93
return false;
94
D = D->getDefinition();
95
// Base class: anyone with AddRef/Release is obviously a refcounted class.
96
if (classHasAddRefRelease(D))
97
return true;
98
99
// Look through all base cases to figure out if the parent is a refcounted
100
// class.
101
for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin();
102
Base != D->bases_end(); ++Base) {
103
bool Super = isClassRefCounted(Base->getType());
104
if (Super) {
105
return true;
106
}
107
}
108
109
return false;
110
}
111
112
inline bool isClassRefCounted(QualType T) {
113
while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
114
T = ArrTy->getElementType();
115
CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
116
return Clazz ? isClassRefCounted(Clazz) : false;
117
}
118
119
inline const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) {
120
for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end();
121
Field != E; ++Field) {
122
if (getNameChecked(Field) == "mRefCnt") {
123
return *Field;
124
}
125
}
126
return 0;
127
}
128
129
inline bool typeHasVTable(QualType T) {
130
while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
131
T = ArrTy->getElementType();
132
CXXRecordDecl *Offender = T->getAsCXXRecordDecl();
133
return Offender && Offender->hasDefinition() && Offender->isDynamicClass();
134
}
135
136
inline std::string getDeclarationNamespace(const Decl *Declaration) {
137
const DeclContext *DC =
138
Declaration->getDeclContext()->getEnclosingNamespaceContext();
139
const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
140
if (!ND) {
141
return "";
142
}
143
144
while (const DeclContext *ParentDC = ND->getParent()) {
145
if (!isa<NamespaceDecl>(ParentDC)) {
146
break;
147
}
148
ND = cast<NamespaceDecl>(ParentDC);
149
}
150
151
const auto &Name = ND->getName();
152
return Name;
153
}
154
155
inline bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) {
156
std::string Name = getDeclarationNamespace(Declaration);
157
if (Name == "") {
158
return false;
159
}
160
161
return Name == "std" || // standard C++ lib
162
Name == "__gnu_cxx" || // gnu C++ lib
163
Name == "boost" || // boost
164
Name == "webrtc" || // upstream webrtc
165
Name == "rtc" || // upstream webrtc 'base' package
166
Name.substr(0, 4) == "icu_" || // icu
167
Name == "google" || // protobuf
168
Name == "google_breakpad" || // breakpad
169
Name == "soundtouch" || // libsoundtouch
170
Name == "stagefright" || // libstagefright
171
Name == "MacFileUtilities" || // MacFileUtilities
172
Name == "dwarf2reader" || // dwarf2reader
173
Name == "arm_ex_to_module" || // arm_ex_to_module
174
Name == "testing" || // gtest
175
Name == "Json" || // jsoncpp
176
Name == "rlbox"; // rlbox
177
}
178
179
inline bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) {
180
std::string Name = getDeclarationNamespace(Declaration);
181
if (Name == "") {
182
return false;
183
}
184
185
return Name == "std" || // standard C++ lib
186
Name == "__gnu_cxx" || // gnu C++ lib
187
Name == "google_breakpad" || // breakpad
188
Name == "testing" || // gtest
189
Name == "rlbox"; // rlbox
190
}
191
192
inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) {
193
Declaration = Declaration->getCanonicalDecl();
194
SourceLocation Loc = Declaration->getLocation();
195
const SourceManager &SM = Declaration->getASTContext().getSourceManager();
196
SmallString<1024> FileName = getFilename(SM, Loc);
197
llvm::sys::fs::make_absolute(FileName);
198
llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
199
End = llvm::sys::path::rend(FileName);
200
for (; Begin != End; ++Begin) {
201
if (Begin->compare_lower(StringRef("graphite2")) == 0) {
202
return true;
203
}
204
if (Begin->compare_lower(StringRef("chromium")) == 0) {
205
// Ignore security/sandbox/chromium but not ipc/chromium.
206
++Begin;
207
return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
208
}
209
}
210
return false;
211
}
212
213
inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call,
214
const SourceManager &SM) {
215
SourceLocation Loc = Call->getBeginLoc();
216
SmallString<1024> FileName = getFilename(SM, Loc);
217
llvm::sys::fs::make_absolute(FileName);
218
llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
219
End = llvm::sys::path::rend(FileName);
220
for (; Begin != End; ++Begin) {
221
if (Begin->compare_lower(StringRef("angle")) == 0 ||
222
Begin->compare_lower(StringRef("chromium")) == 0 ||
223
Begin->compare_lower(StringRef("crashreporter")) == 0 ||
224
Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
225
Begin->compare_lower(StringRef("gflags")) == 0 ||
226
Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
227
Begin->compare_lower(StringRef("icu")) == 0 ||
228
Begin->compare_lower(StringRef("jsoncpp")) == 0 ||
229
Begin->compare_lower(StringRef("libstagefright")) == 0 ||
230
Begin->compare_lower(StringRef("mtransport")) == 0 ||
231
Begin->compare_lower(StringRef("protobuf")) == 0 ||
232
Begin->compare_lower(StringRef("skia")) == 0 ||
233
Begin->compare_lower(StringRef("sfntly")) == 0 ||
234
// Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
235
Begin->compare_lower(StringRef("testing")) == 0) {
236
return true;
237
}
238
if (Begin->compare_lower(StringRef("webrtc")) == 0) {
239
// Ignore trunk/webrtc, but not media/webrtc
240
++Begin;
241
return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0;
242
}
243
}
244
return false;
245
}
246
247
inline bool isInterestingDeclForImplicitConversion(const Decl *Declaration) {
248
return !isInIgnoredNamespaceForImplicitConversion(Declaration) &&
249
!isIgnoredPathForImplicitConversion(Declaration);
250
}
251
252
inline bool isIgnoredExprForMustUse(const Expr *E) {
253
if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
254
switch (OpCall->getOperator()) {
255
case OO_Equal:
256
case OO_PlusEqual:
257
case OO_MinusEqual:
258
case OO_StarEqual:
259
case OO_SlashEqual:
260
case OO_PercentEqual:
261
case OO_CaretEqual:
262
case OO_AmpEqual:
263
case OO_PipeEqual:
264
case OO_LessLessEqual:
265
case OO_GreaterGreaterEqual:
266
return true;
267
default:
268
return false;
269
}
270
}
271
272
if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
273
return Op->isAssignmentOp();
274
}
275
276
return false;
277
}
278
279
inline bool typeIsRefPtr(QualType Q) {
280
CXXRecordDecl *D = Q->getAsCXXRecordDecl();
281
if (!D || !D->getIdentifier()) {
282
return false;
283
}
284
285
StringRef name = D->getName();
286
if (name == "RefPtr" || name == "nsCOMPtr") {
287
return true;
288
}
289
return false;
290
}
291
292
// The method defined in clang for ignoring implicit nodes doesn't work with
293
// some AST trees. To get around this, we define our own implementation of
294
// IgnoreTrivials.
295
inline const Stmt *MaybeSkipOneTrivial(const Stmt *s) {
296
if (!s) {
297
return nullptr;
298
}
299
if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
300
return ewc->getSubExpr();
301
}
302
if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
303
// With clang 10 and up `getTemporary` has been replaced with the more
304
// versatile `getSubExpr`.
305
#if CLANG_VERSION_FULL >= 1000
306
return mte->getSubExpr();
307
#else
308
return mte->GetTemporaryExpr();
309
#endif
310
}
311
if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
312
return bte->getSubExpr();
313
}
314
if (auto *ce = dyn_cast<CastExpr>(s)) {
315
s = ce->getSubExpr();
316
}
317
if (auto *pe = dyn_cast<ParenExpr>(s)) {
318
s = pe->getSubExpr();
319
}
320
// Not a trivial.
321
return s;
322
}
323
324
inline const Stmt *IgnoreTrivials(const Stmt *s) {
325
while (true) {
326
const Stmt* newS = MaybeSkipOneTrivial(s);
327
if (newS == s) {
328
return newS;
329
}
330
s = newS;
331
}
332
333
// Unreachable
334
return nullptr;
335
}
336
337
inline const Expr *IgnoreTrivials(const Expr *e) {
338
return cast_or_null<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e)));
339
}
340
341
// Returns the input if the input is not a trivial.
342
inline const Expr *MaybeSkipOneTrivial(const Expr *e) {
343
return cast_or_null<Expr>(MaybeSkipOneTrivial(static_cast<const Stmt *>(e)));
344
}
345
346
const FieldDecl *getBaseRefCntMember(QualType T);
347
348
inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
349
const FieldDecl *RefCntMember = getClassRefCntMember(D);
350
if (RefCntMember && isClassRefCounted(D)) {
351
return RefCntMember;
352
}
353
354
for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(),
355
E = D->bases_end();
356
Base != E; ++Base) {
357
RefCntMember = getBaseRefCntMember(Base->getType());
358
if (RefCntMember) {
359
return RefCntMember;
360
}
361
}
362
return 0;
363
}
364
365
inline const FieldDecl *getBaseRefCntMember(QualType T) {
366
while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
367
T = ArrTy->getElementType();
368
CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
369
return Clazz ? getBaseRefCntMember(Clazz) : 0;
370
}
371
372
inline bool isPlacementNew(const CXXNewExpr *Expression) {
373
// Regular new expressions aren't placement new
374
if (Expression->getNumPlacementArgs() == 0)
375
return false;
376
const FunctionDecl *Declaration = Expression->getOperatorNew();
377
if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) {
378
return false;
379
}
380
return true;
381
}
382
383
extern DenseMap<StringRef, bool> InThirdPartyPathCache;
384
385
inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) {
386
StringRef OriginalFileName = getFilename(SM, Loc);
387
auto pair = InThirdPartyPathCache.find(OriginalFileName);
388
if (pair != InThirdPartyPathCache.end()) {
389
return pair->second;
390
}
391
392
SmallString<1024> FileName = OriginalFileName;
393
llvm::sys::fs::make_absolute(FileName);
394
395
for (uint32_t i = 0; i < MOZ_THIRD_PARTY_PATHS_COUNT; ++i) {
396
auto PathB = sys::path::begin(FileName);
397
auto PathE = sys::path::end(FileName);
398
399
auto ThirdPartyB = sys::path::begin(MOZ_THIRD_PARTY_PATHS[i]);
400
auto ThirdPartyE = sys::path::end(MOZ_THIRD_PARTY_PATHS[i]);
401
402
for (; PathB != PathE; ++PathB) {
403
// Perform an inner loop to compare path segments, checking if the current
404
// segment is the start of the current third party path.
405
auto IPathB = PathB;
406
auto IThirdPartyB = ThirdPartyB;
407
for (; IPathB != PathE && IThirdPartyB != ThirdPartyE;
408
++IPathB, ++IThirdPartyB) {
409
if (IPathB->compare_lower(*IThirdPartyB) != 0) {
410
break;
411
}
412
}
413
414
// We found a match!
415
if (IThirdPartyB == ThirdPartyE) {
416
InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, true));
417
return true;
418
}
419
}
420
}
421
422
InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, false));
423
return false;
424
}
425
426
inline bool inThirdPartyPath(const Decl *D, ASTContext *context) {
427
D = D->getCanonicalDecl();
428
SourceLocation Loc = D->getLocation();
429
const SourceManager &SM = context->getSourceManager();
430
431
return inThirdPartyPath(Loc, SM);
432
}
433
434
inline CXXRecordDecl *getNonTemplateSpecializedCXXRecordDecl(QualType Q) {
435
auto *D = Q->getAsCXXRecordDecl();
436
437
if (!D) {
438
auto TemplateQ = Q->getAs<TemplateSpecializationType>();
439
if (!TemplateQ) {
440
return nullptr;
441
}
442
443
auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl();
444
if (!TemplateDecl) {
445
return nullptr;
446
}
447
448
D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl());
449
if (!D) {
450
return nullptr;
451
}
452
}
453
454
return D;
455
}
456
457
inline bool inThirdPartyPath(const Decl *D) {
458
return inThirdPartyPath(D, &D->getASTContext());
459
}
460
461
inline bool inThirdPartyPath(const Stmt *S, ASTContext *context) {
462
SourceLocation Loc = S->getBeginLoc();
463
const SourceManager &SM = context->getSourceManager();
464
auto ExpansionLoc = SM.getExpansionLoc(Loc);
465
if (ExpansionLoc.isInvalid()) {
466
return inThirdPartyPath(Loc, SM);
467
}
468
return inThirdPartyPath(ExpansionLoc, SM);
469
}
470
471
/// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp()
472
inline bool isInfixBinaryOp(const CXXOperatorCallExpr *OpCall) {
473
#if CLANG_VERSION_FULL >= 400
474
return OpCall->isInfixBinaryOp();
475
#else
476
// Taken from clang source.
477
if (OpCall->getNumArgs() != 2)
478
return false;
479
480
switch (OpCall->getOperator()) {
481
case OO_Call:
482
case OO_Subscript:
483
return false;
484
default:
485
return true;
486
}
487
#endif
488
}
489
490
#endif