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 CustomMatchers_h__
6
#define CustomMatchers_h__
7
8
#include "MemMoveAnnotation.h"
9
#include "Utils.h"
10
11
namespace clang {
12
namespace ast_matchers {
13
14
/// This matcher will match any function declaration that is declared as a heap
15
/// allocator.
16
AST_MATCHER(FunctionDecl, heapAllocator) {
17
return hasCustomAttribute<moz_heap_allocator>(&Node);
18
}
19
20
/// This matcher will match any declaration that is marked as not accepting
21
/// arithmetic expressions in its arguments.
22
AST_MATCHER(Decl, noArithmeticExprInArgs) {
23
return hasCustomAttribute<moz_no_arith_expr_in_arg>(&Node);
24
}
25
26
/// This matcher will match any C++ class that is marked as having a trivial
27
/// constructor and destructor.
28
AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
29
return hasCustomAttribute<moz_trivial_ctor_dtor>(&Node);
30
}
31
32
/// This matcher will match any C++ class that is marked as having a trivial
33
/// destructor.
34
AST_MATCHER(CXXRecordDecl, hasTrivialDtor) {
35
return hasCustomAttribute<moz_trivial_dtor>(&Node);
36
}
37
38
AST_MATCHER(CXXConstructExpr, allowsTemporary) {
39
return hasCustomAttribute<moz_allow_temporary>(Node.getConstructor());
40
}
41
42
/// This matcher will match lvalue-ref-qualified methods.
43
AST_MATCHER(CXXMethodDecl, isLValueRefQualified) {
44
return Node.getRefQualifier() == RQ_LValue;
45
}
46
47
/// This matcher will match rvalue-ref-qualified methods.
48
AST_MATCHER(CXXMethodDecl, isRValueRefQualified) {
49
return Node.getRefQualifier() == RQ_RValue;
50
}
51
52
AST_POLYMORPHIC_MATCHER(isFirstParty,
53
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
54
return !inThirdPartyPath(&Node, &Finder->getASTContext()) &&
55
!ASTIsInSystemHeader(Finder->getASTContext(), Node);
56
}
57
58
/// This matcher will match temporary expressions.
59
/// We need this matcher for compatibility with clang 3.* (clang 4 and above
60
/// insert a MaterializeTemporaryExpr everywhere).
61
AST_MATCHER(Expr, isTemporary) {
62
return Node.isRValue() || Node.isXValue() ||
63
isa<MaterializeTemporaryExpr>(&Node);
64
}
65
66
/// This matcher will match any method declaration that is marked as returning
67
/// a pointer deleted by the destructor of the class.
68
AST_MATCHER(CXXMethodDecl, noDanglingOnTemporaries) {
69
return hasCustomAttribute<moz_no_dangling_on_temporaries>(&Node);
70
}
71
72
/// This matcher will match any function declaration that is marked to prohibit
73
/// calling AddRef or Release on its return value.
74
AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
75
return hasCustomAttribute<moz_no_addref_release_on_return>(&Node);
76
}
77
78
/// This matcher will match any function declaration that is marked as being
79
/// allowed to run script.
80
AST_MATCHER(FunctionDecl, hasCanRunScriptAnnotation) {
81
return hasCustomAttribute<moz_can_run_script>(&Node);
82
}
83
84
/// This matcher will match all arithmetic binary operators.
85
AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
86
BinaryOperatorKind OpCode = Node.getOpcode();
87
return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem ||
88
OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl ||
89
OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor ||
90
OpCode == BO_Or || OpCode == BO_MulAssign || OpCode == BO_DivAssign ||
91
OpCode == BO_RemAssign || OpCode == BO_AddAssign ||
92
OpCode == BO_SubAssign || OpCode == BO_ShlAssign ||
93
OpCode == BO_ShrAssign || OpCode == BO_AndAssign ||
94
OpCode == BO_XorAssign || OpCode == BO_OrAssign;
95
}
96
97
/// This matcher will match all arithmetic unary operators.
98
AST_MATCHER(UnaryOperator, unaryArithmeticOperator) {
99
UnaryOperatorKind OpCode = Node.getOpcode();
100
return OpCode == UO_PostInc || OpCode == UO_PostDec || OpCode == UO_PreInc ||
101
OpCode == UO_PreDec || OpCode == UO_Plus || OpCode == UO_Minus ||
102
OpCode == UO_Not;
103
}
104
105
/// This matcher will match the unary dereference operator
106
AST_MATCHER(UnaryOperator, unaryDereferenceOperator) {
107
UnaryOperatorKind OpCode = Node.getOpcode();
108
return OpCode == UO_Deref;
109
}
110
111
/// This matcher will match == and != binary operators.
112
AST_MATCHER(BinaryOperator, binaryEqualityOperator) {
113
BinaryOperatorKind OpCode = Node.getOpcode();
114
return OpCode == BO_EQ || OpCode == BO_NE;
115
}
116
117
/// This matcher will match comma operator.
118
AST_MATCHER(BinaryOperator, binaryCommaOperator) {
119
BinaryOperatorKind OpCode = Node.getOpcode();
120
return OpCode == BO_Comma;
121
}
122
123
/// This matcher will match floating point types.
124
AST_MATCHER(QualType, isFloat) { return Node->isRealFloatingType(); }
125
126
/// This matcher will match locations in system headers. This is adopted from
127
/// isExpansionInSystemHeader in newer clangs, but modified in order to work
128
/// with old clangs that we use on infra.
129
AST_POLYMORPHIC_MATCHER(isInSystemHeader, \
130
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
131
return ASTIsInSystemHeader(Finder->getASTContext(), Node);
132
}
133
134
/// This matcher will match a list of files. These files contain
135
/// known NaN-testing expressions which we would like to whitelist.
136
AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) {
137
const char *whitelist[] = {"SkScalar.h", "json_writer.cpp", "State.cpp"};
138
139
SourceLocation Loc = Node.getOperatorLoc();
140
StringRef FileName =
141
getFilename(Finder->getASTContext().getSourceManager(), Loc);
142
for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) {
143
if (llvm::sys::path::rbegin(FileName)->equals(*itr)) {
144
return true;
145
}
146
}
147
148
return false;
149
}
150
151
/// This matcher will match all accesses to AddRef or Release methods.
152
AST_MATCHER(MemberExpr, isAddRefOrRelease) {
153
ValueDecl *Member = Node.getMemberDecl();
154
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
155
if (Method) {
156
const auto &Name = getNameChecked(Method);
157
return Name == "AddRef" || Name == "Release";
158
}
159
return false;
160
}
161
162
/// This matcher will select classes which are refcounted AND have an mRefCnt
163
/// member.
164
AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
165
return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
166
}
167
168
/// This matcher will select classes which are refcounted.
169
AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
170
171
AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
172
173
AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
174
return hasCustomAttribute<moz_needs_no_vtable_type>(&Node);
175
}
176
177
/// This matcher will select classes which are non-memmovable
178
AST_MATCHER(QualType, isNonMemMovable) {
179
return NonMemMovable.hasEffectiveAnnotation(Node);
180
}
181
182
/// This matcher will select classes which require a memmovable template arg
183
AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
184
return hasCustomAttribute<moz_needs_memmovable_type>(&Node);
185
}
186
187
/// This matcher will select classes which require all members to be memmovable
188
AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
189
return hasCustomAttribute<moz_needs_memmovable_members>(&Node);
190
}
191
192
AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
193
const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
194
return
195
// Skip constructors in system headers
196
!ASTIsInSystemHeader(Declaration->getASTContext(), *Declaration) &&
197
// Skip ignored namespaces and paths
198
!isInIgnoredNamespaceForImplicitCtor(Declaration) &&
199
!inThirdPartyPath(Declaration) &&
200
// We only want Converting constructors
201
Declaration->isConvertingConstructor(false) &&
202
// We don't want copy of move constructors, as those are allowed to be
203
// implicit
204
!Declaration->isCopyOrMoveConstructor() &&
205
// We don't want inheriting constructors, since using declarations can't
206
// have attributes
207
!Declaration->isInheritingConstructor() &&
208
// We don't want deleted constructors.
209
!Declaration->isDeleted();
210
}
211
212
AST_MATCHER_P(Expr, ignoreTrivials, internal::Matcher<Expr>, InnerMatcher) {
213
return InnerMatcher.matches(*IgnoreTrivials(&Node), Finder, Builder);
214
}
215
216
// Takes two matchers: the first one is a condition; the second is a matcher to be
217
// applied once we are done unwrapping trivials. While the condition does not match
218
// and we're looking at a trivial, will keep unwrapping the trivial and trying again.
219
// Once the condition matches, we will go ahead and unwrap all trivials and apply the
220
// inner matcher to the result.
221
//
222
// The expected use here is if we want to condition a match on some typecheck but
223
// apply the match to only non-trivials, because there are trivials (e.g. casts) that
224
// can change types.
225
AST_MATCHER_P2(Expr, ignoreTrivialsConditional,
226
internal::Matcher<Expr>, Condition,
227
internal::Matcher<Expr>, InnerMatcher) {
228
const Expr *node = &Node;
229
while (true) {
230
if (Condition.matches(*node, Finder, Builder)) {
231
return InnerMatcher.matches(*IgnoreTrivials(node), Finder, Builder);
232
}
233
const Expr *newNode = MaybeSkipOneTrivial(node);
234
if (newNode == node) {
235
return false;
236
}
237
node = newNode;
238
}
239
}
240
241
// We can't call this "isImplicit" since it clashes with an existing matcher in
242
// clang.
243
AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) {
244
return hasCustomAttribute<moz_implicit>(&Node);
245
}
246
247
AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
248
249
AST_MATCHER(QualType, autoNonAutoableType) {
250
if (const AutoType *T = Node->getContainedAutoType()) {
251
if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) {
252
return hasCustomAttribute<moz_non_autoable>(Rec);
253
}
254
}
255
return false;
256
}
257
258
AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
259
return Node.isExplicit() && Node.isMoveConstructor();
260
}
261
262
AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
263
return !Node.isUserProvided() && Node.isCopyConstructor();
264
}
265
266
AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) {
267
static const std::string AssertName = "MOZ_AssertAssignmentTest";
268
const FunctionDecl *Method = Node.getDirectCallee();
269
270
return Method && Method->getDeclName().isIdentifier() &&
271
Method->getName() == AssertName;
272
}
273
274
AST_MATCHER(CallExpr, isSnprintfLikeFunc) {
275
static const std::string Snprintf = "snprintf";
276
static const std::string Vsnprintf = "vsnprintf";
277
const FunctionDecl *Func = Node.getDirectCallee();
278
279
if (!Func || isa<CXXMethodDecl>(Func)) {
280
return false;
281
}
282
283
StringRef Name = getNameChecked(Func);
284
if (Name != Snprintf && Name != Vsnprintf) {
285
return false;
286
}
287
288
return !inThirdPartyPath(Node.getBeginLoc(),
289
Finder->getASTContext().getSourceManager()) &&
290
!isIgnoredPathForSprintfLiteral(
291
&Node, Finder->getASTContext().getSourceManager());
292
}
293
294
AST_MATCHER(CXXRecordDecl, isLambdaDecl) { return Node.isLambda(); }
295
296
AST_MATCHER(QualType, isRefPtr) { return typeIsRefPtr(Node); }
297
298
AST_MATCHER(QualType, isSmartPtrToRefCounted) {
299
auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
300
if (!D) {
301
return false;
302
}
303
304
D = D->getCanonicalDecl();
305
306
return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
307
}
308
309
AST_MATCHER(ClassTemplateSpecializationDecl, isSmartPtrToRefCountedDecl) {
310
auto *D = dyn_cast_or_null<CXXRecordDecl>(Node.getSpecializedTemplate()->getTemplatedDecl());
311
if (!D) {
312
return false;
313
}
314
315
D = D->getCanonicalDecl();
316
317
return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
318
}
319
320
321
AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
322
const CXXRecordDecl *Decl = Node.getCanonicalDecl();
323
324
// Must have definition and should inherit other classes
325
return Decl && Decl->hasDefinition() && Decl->getNumBases();
326
}
327
328
AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
329
const CXXMethodDecl *Decl = Node.getCanonicalDecl();
330
return Decl && hasCustomAttribute<moz_required_base_method>(Decl);
331
}
332
333
AST_MATCHER(CXXMethodDecl, isNonVirtual) {
334
const CXXMethodDecl *Decl = Node.getCanonicalDecl();
335
return Decl && !Decl->isVirtual();
336
}
337
338
AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
339
const FunctionDecl *Decl = Node.getCanonicalDecl();
340
return Decl && hasCustomAttribute<moz_must_return_from_caller_if_this_is_arg>(Decl);
341
}
342
343
/// This matcher will select default args which have nullptr as the value.
344
AST_MATCHER(CXXDefaultArgExpr, isNullDefaultArg) {
345
const Expr *Expr = Node.getExpr();
346
return Expr && Expr->isNullPointerConstant(Finder->getASTContext(),
347
Expr::NPC_NeverValueDependent);
348
}
349
350
AST_MATCHER(UsingDirectiveDecl, isUsingNamespaceMozillaJava) {
351
const NamespaceDecl *Namespace = Node.getNominatedNamespace();
352
const std::string &FQName = Namespace->getQualifiedNameAsString();
353
354
static const char NAMESPACE[] = "mozilla::java";
355
static const char PREFIX[] = "mozilla::java::";
356
357
// We match both the `mozilla::java` namespace itself as well as any other
358
// namespaces contained within the `mozilla::java` namespace.
359
return !FQName.compare(NAMESPACE) ||
360
!FQName.compare(0, sizeof(PREFIX) - 1, PREFIX);
361
}
362
363
} // namespace ast_matchers
364
} // namespace clang
365
366
#endif