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/. */
5 #ifndef CustomMatchers_h__
6 #define CustomMatchers_h__
8 #include "MemMoveAnnotation.h"
11 #if CLANG_VERSION_FULL >= 1300
12 // Starting with clang-13 Expr::isRValue has been renamed to Expr::isPRValue
13 #define isRValue isPRValue
17 namespace ast_matchers
{
19 /// This matcher will match any function declaration that is declared as a heap
21 AST_MATCHER(FunctionDecl
, heapAllocator
) {
22 return hasCustomAttribute
<moz_heap_allocator
>(&Node
);
25 /// This matcher will match any declaration that is marked as not accepting
26 /// arithmetic expressions in its arguments.
27 AST_MATCHER(Decl
, noArithmeticExprInArgs
) {
28 return hasCustomAttribute
<moz_no_arith_expr_in_arg
>(&Node
);
31 /// This matcher will match any C++ class that is marked as having a trivial
32 /// constructor and destructor.
33 AST_MATCHER(CXXRecordDecl
, hasTrivialCtorDtor
) {
34 return hasCustomAttribute
<moz_trivial_ctor_dtor
>(&Node
);
37 /// This matcher will match any C++ class that is marked as having a trivial
39 AST_MATCHER(CXXRecordDecl
, hasTrivialDtor
) {
40 return hasCustomAttribute
<moz_trivial_dtor
>(&Node
);
43 AST_MATCHER(CXXConstructExpr
, allowsTemporary
) {
44 return hasCustomAttribute
<moz_allow_temporary
>(Node
.getConstructor());
47 /// This matcher will match lvalue-ref-qualified methods.
48 AST_MATCHER(CXXMethodDecl
, isLValueRefQualified
) {
49 return Node
.getRefQualifier() == RQ_LValue
;
52 /// This matcher will match rvalue-ref-qualified methods.
53 AST_MATCHER(CXXMethodDecl
, isRValueRefQualified
) {
54 return Node
.getRefQualifier() == RQ_RValue
;
57 AST_POLYMORPHIC_MATCHER(isFirstParty
,
58 AST_POLYMORPHIC_SUPPORTED_TYPES(Decl
, Stmt
)) {
59 return !inThirdPartyPath(&Node
, &Finder
->getASTContext()) &&
60 !ASTIsInSystemHeader(Finder
->getASTContext(), Node
);
63 AST_MATCHER(DeclaratorDecl
, isNotSpiderMonkey
) {
64 // Detect SpiderMonkey path. Not as strict as isFirstParty, but this is
65 // expected to disappear soon by getting a common style guide between DOM and
67 std::string Path
= Node
.getBeginLoc().printToString(
68 Finder
->getASTContext().getSourceManager());
69 return Path
.find("js") == std::string::npos
&&
70 Path
.find("xpc") == std::string::npos
&&
71 Path
.find("XPC") == std::string::npos
;
74 /// This matcher will match temporary expressions.
75 /// We need this matcher for compatibility with clang 3.* (clang 4 and above
76 /// insert a MaterializeTemporaryExpr everywhere).
77 AST_MATCHER(Expr
, isTemporary
) {
78 return Node
.isRValue() || Node
.isXValue() ||
79 isa
<MaterializeTemporaryExpr
>(&Node
);
82 /// This matcher will match any method declaration that is marked as returning
83 /// a pointer deleted by the destructor of the class.
84 AST_MATCHER(CXXMethodDecl
, noDanglingOnTemporaries
) {
85 return hasCustomAttribute
<moz_no_dangling_on_temporaries
>(&Node
);
88 /// This matcher will match any function declaration that is marked to prohibit
89 /// calling AddRef or Release on its return value.
90 AST_MATCHER(FunctionDecl
, hasNoAddRefReleaseOnReturnAttr
) {
91 return hasCustomAttribute
<moz_no_addref_release_on_return
>(&Node
);
94 /// This matcher will match any function declaration that is marked as being
95 /// allowed to run script.
96 AST_MATCHER(FunctionDecl
, hasCanRunScriptAnnotation
) {
97 return hasCustomAttribute
<moz_can_run_script
>(&Node
);
100 /// This matcher will match all arithmetic binary operators.
101 AST_MATCHER(BinaryOperator
, binaryArithmeticOperator
) {
102 BinaryOperatorKind OpCode
= Node
.getOpcode();
103 return OpCode
== BO_Mul
|| OpCode
== BO_Div
|| OpCode
== BO_Rem
||
104 OpCode
== BO_Add
|| OpCode
== BO_Sub
|| OpCode
== BO_Shl
||
105 OpCode
== BO_Shr
|| OpCode
== BO_And
|| OpCode
== BO_Xor
||
106 OpCode
== BO_Or
|| OpCode
== BO_MulAssign
|| OpCode
== BO_DivAssign
||
107 OpCode
== BO_RemAssign
|| OpCode
== BO_AddAssign
||
108 OpCode
== BO_SubAssign
|| OpCode
== BO_ShlAssign
||
109 OpCode
== BO_ShrAssign
|| OpCode
== BO_AndAssign
||
110 OpCode
== BO_XorAssign
|| OpCode
== BO_OrAssign
;
113 /// This matcher will match all arithmetic unary operators.
114 AST_MATCHER(UnaryOperator
, unaryArithmeticOperator
) {
115 UnaryOperatorKind OpCode
= Node
.getOpcode();
116 return OpCode
== UO_PostInc
|| OpCode
== UO_PostDec
|| OpCode
== UO_PreInc
||
117 OpCode
== UO_PreDec
|| OpCode
== UO_Plus
|| OpCode
== UO_Minus
||
121 /// This matcher will match the unary dereference operator
122 AST_MATCHER(UnaryOperator
, unaryDereferenceOperator
) {
123 UnaryOperatorKind OpCode
= Node
.getOpcode();
124 return OpCode
== UO_Deref
;
127 /// This matcher will match == and != binary operators.
128 AST_MATCHER(BinaryOperator
, binaryEqualityOperator
) {
129 BinaryOperatorKind OpCode
= Node
.getOpcode();
130 return OpCode
== BO_EQ
|| OpCode
== BO_NE
;
133 /// This matcher will match comma operator.
134 AST_MATCHER(BinaryOperator
, binaryCommaOperator
) {
135 BinaryOperatorKind OpCode
= Node
.getOpcode();
136 return OpCode
== BO_Comma
;
139 /// This matcher will match floating point types.
140 AST_MATCHER(QualType
, isFloat
) { return Node
->isRealFloatingType(); }
142 /// This matcher will match locations in system headers. This is adopted from
143 /// isExpansionInSystemHeader in newer clangs, but modified in order to work
144 /// with old clangs that we use on infra.
145 AST_POLYMORPHIC_MATCHER(isInSystemHeader
,
146 AST_POLYMORPHIC_SUPPORTED_TYPES(Decl
, Stmt
)) {
147 return ASTIsInSystemHeader(Finder
->getASTContext(), Node
);
150 /// This matcher will match a file "gtest-port.h". The file contains
151 /// known fopen usages that are OK.
152 AST_MATCHER(CallExpr
, isInWhitelistForFopenUsage
) {
153 static const char Whitelist
[] = "gtest-port.h";
154 SourceLocation Loc
= Node
.getBeginLoc();
156 getFilename(Finder
->getASTContext().getSourceManager(), Loc
);
158 return llvm::sys::path::rbegin(FileName
)->equals(Whitelist
);
161 /// This matcher will match a list of files. These files contain
162 /// known NaN-testing expressions which we would like to whitelist.
163 AST_MATCHER(BinaryOperator
, isInWhitelistForNaNExpr
) {
164 const char *whitelist
[] = {"SkScalar.h", "json_writer.cpp", "State.cpp"};
166 SourceLocation Loc
= Node
.getOperatorLoc();
168 getFilename(Finder
->getASTContext().getSourceManager(), Loc
);
169 for (auto itr
= std::begin(whitelist
); itr
!= std::end(whitelist
); itr
++) {
170 if (llvm::sys::path::rbegin(FileName
)->equals(*itr
)) {
178 AST_MATCHER(CallExpr
, isInWhiteListForPrincipalGetUri
) {
179 const auto Whitelist
= {"nsIPrincipal.h", "BasePrincipal.cpp",
180 "ContentPrincipal.cpp"};
181 SourceLocation Loc
= Node
.getBeginLoc();
183 getFilename(Finder
->getASTContext().getSourceManager(), Loc
);
185 for (auto Exclusion
: Whitelist
) {
186 if (Filename
.find(Exclusion
) != std::string::npos
) {
193 /// This matcher will match a list of files which contain NS_NewNamedThread
194 /// code or names of existing threads that we would like to ignore.
195 AST_MATCHER(CallExpr
, isInAllowlistForThreads
) {
197 // Get the source location of the call.
198 SourceLocation Loc
= Node
.getRParenLoc();
200 getFilename(Finder
->getASTContext().getSourceManager(), Loc
);
202 const auto rbegin
= [](StringRef s
) { return llvm::sys::path::rbegin(s
); };
203 const auto rend
= [](StringRef s
) { return llvm::sys::path::rend(s
); };
205 // Files in the allowlist are (definitionally) explicitly permitted to create
207 for (auto thread_file
: allow_thread_files
) {
208 // All the provided path-elements must match.
209 const bool match
= [&] {
210 auto it1
= rbegin(FileName
), it2
= rbegin(thread_file
),
211 end1
= rend(FileName
), end2
= rend(thread_file
);
212 for (; it2
!= end2
; ++it1
, ++it2
) {
213 if (it1
== end1
|| !it1
->equals(*it2
)) {
224 // Check the first arg (the name of the thread).
225 const StringLiteral
*nameArg
=
226 dyn_cast
<StringLiteral
>(Node
.getArg(0)->IgnoreImplicit());
228 const StringRef name
= nameArg
->getString();
229 for (auto thread_name
: allow_thread_names
) {
230 if (name
.equals(thread_name
)) {
239 /// This matcher will match all accesses to AddRef or Release methods.
240 AST_MATCHER(MemberExpr
, isAddRefOrRelease
) {
241 ValueDecl
*Member
= Node
.getMemberDecl();
242 CXXMethodDecl
*Method
= dyn_cast
<CXXMethodDecl
>(Member
);
244 const auto &Name
= getNameChecked(Method
);
245 return Name
== "AddRef" || Name
== "Release";
250 /// This matcher will select classes which are refcounted AND have an mRefCnt
252 AST_MATCHER(CXXRecordDecl
, hasRefCntMember
) {
253 return isClassRefCounted(&Node
) && getClassRefCntMember(&Node
);
256 /// This matcher will select classes which are refcounted.
257 AST_MATCHER(CXXRecordDecl
, isRefCounted
) { return isClassRefCounted(&Node
); }
259 AST_MATCHER(QualType
, hasVTable
) { return typeHasVTable(Node
); }
261 AST_MATCHER(CXXRecordDecl
, hasNeedsNoVTableTypeAttr
) {
262 return hasCustomAttribute
<moz_needs_no_vtable_type
>(&Node
);
265 /// This matcher will select classes which are non-memmovable
266 AST_MATCHER(QualType
, isNonMemMovable
) {
267 return NonMemMovable
.hasEffectiveAnnotation(Node
);
270 /// This matcher will select classes which require a memmovable template arg
271 AST_MATCHER(CXXRecordDecl
, needsMemMovableTemplateArg
) {
272 return hasCustomAttribute
<moz_needs_memmovable_type
>(&Node
);
275 /// This matcher will select classes which require all members to be memmovable
276 AST_MATCHER(CXXRecordDecl
, needsMemMovableMembers
) {
277 return hasCustomAttribute
<moz_needs_memmovable_members
>(&Node
);
280 AST_MATCHER(CXXConstructorDecl
, isInterestingImplicitCtor
) {
281 const CXXConstructorDecl
*Declaration
= Node
.getCanonicalDecl();
283 // Skip constructors in system headers
284 !ASTIsInSystemHeader(Declaration
->getASTContext(), *Declaration
) &&
285 // Skip ignored namespaces and paths
286 !isInIgnoredNamespaceForImplicitCtor(Declaration
) &&
287 !inThirdPartyPath(Declaration
) &&
288 // We only want Converting constructors
289 Declaration
->isConvertingConstructor(false) &&
290 // We don't want copy of move constructors, as those are allowed to be
292 !Declaration
->isCopyOrMoveConstructor() &&
293 // We don't want inheriting constructors, since using declarations can't
295 !Declaration
->isInheritingConstructor() &&
296 // We don't want deleted constructors.
297 !Declaration
->isDeleted();
300 AST_MATCHER_P(Expr
, ignoreTrivials
, internal::Matcher
<Expr
>, InnerMatcher
) {
301 return InnerMatcher
.matches(*IgnoreTrivials(&Node
), Finder
, Builder
);
304 // Takes two matchers: the first one is a condition; the second is a matcher to
305 // be applied once we are done unwrapping trivials. While the condition does
306 // not match and we're looking at a trivial, will keep unwrapping the trivial
307 // and trying again. Once the condition matches, we will go ahead and unwrap all
308 // trivials and apply the inner matcher to the result.
310 // The expected use here is if we want to condition a match on some typecheck
311 // but apply the match to only non-trivials, because there are trivials (e.g.
312 // casts) that can change types.
313 AST_MATCHER_P2(Expr
, ignoreTrivialsConditional
, internal::Matcher
<Expr
>,
314 Condition
, internal::Matcher
<Expr
>, InnerMatcher
) {
315 const Expr
*node
= &Node
;
317 if (Condition
.matches(*node
, Finder
, Builder
)) {
318 return InnerMatcher
.matches(*IgnoreTrivials(node
), Finder
, Builder
);
320 const Expr
*newNode
= MaybeSkipOneTrivial(node
);
321 if (newNode
== node
) {
328 // We can't call this "isImplicit" since it clashes with an existing matcher in
330 AST_MATCHER(CXXConstructorDecl
, isMarkedImplicit
) {
331 return hasCustomAttribute
<moz_implicit
>(&Node
);
334 AST_MATCHER(CXXRecordDecl
, isConcreteClass
) { return !Node
.isAbstract(); }
336 AST_MATCHER(QualType
, autoNonAutoableType
) {
337 if (const AutoType
*T
= Node
->getContainedAutoType()) {
338 if (const CXXRecordDecl
*Rec
= T
->getAsCXXRecordDecl()) {
339 return hasCustomAttribute
<moz_non_autoable
>(Rec
);
345 AST_MATCHER(CXXConstructorDecl
, isExplicitMoveConstructor
) {
346 return Node
.isExplicit() && Node
.isMoveConstructor();
349 AST_MATCHER(CXXConstructorDecl
, isCompilerProvidedCopyConstructor
) {
350 return !Node
.isUserProvided() && Node
.isCopyConstructor();
353 AST_MATCHER(CallExpr
, isAssertAssignmentTestFunc
) {
354 static const std::string AssertName
= "MOZ_AssertAssignmentTest";
355 const FunctionDecl
*Method
= Node
.getDirectCallee();
357 return Method
&& Method
->getDeclName().isIdentifier() &&
358 Method
->getName() == AssertName
;
361 AST_MATCHER(CallExpr
, isSnprintfLikeFunc
) {
362 static const std::string Snprintf
= "snprintf";
363 static const std::string Vsnprintf
= "vsnprintf";
364 const FunctionDecl
*Func
= Node
.getDirectCallee();
366 if (!Func
|| isa
<CXXMethodDecl
>(Func
)) {
370 StringRef Name
= getNameChecked(Func
);
371 if (Name
!= Snprintf
&& Name
!= Vsnprintf
) {
375 return !inThirdPartyPath(Node
.getBeginLoc(),
376 Finder
->getASTContext().getSourceManager()) &&
377 !isIgnoredPathForSprintfLiteral(
378 &Node
, Finder
->getASTContext().getSourceManager());
381 AST_MATCHER(CXXRecordDecl
, isLambdaDecl
) { return Node
.isLambda(); }
383 AST_MATCHER(QualType
, isRefPtr
) { return typeIsRefPtr(Node
); }
385 AST_MATCHER(QualType
, isSmartPtrToRefCounted
) {
386 auto *D
= getNonTemplateSpecializedCXXRecordDecl(Node
);
391 D
= D
->getCanonicalDecl();
393 return D
&& hasCustomAttribute
<moz_is_smartptr_to_refcounted
>(D
);
396 AST_MATCHER(ClassTemplateSpecializationDecl
, isSmartPtrToRefCountedDecl
) {
397 auto *D
= dyn_cast_or_null
<CXXRecordDecl
>(
398 Node
.getSpecializedTemplate()->getTemplatedDecl());
403 D
= D
->getCanonicalDecl();
405 return D
&& hasCustomAttribute
<moz_is_smartptr_to_refcounted
>(D
);
408 AST_MATCHER(CXXRecordDecl
, hasBaseClasses
) {
409 const CXXRecordDecl
*Decl
= Node
.getCanonicalDecl();
411 // Must have definition and should inherit other classes
412 return Decl
&& Decl
->hasDefinition() && Decl
->getNumBases();
415 AST_MATCHER(CXXMethodDecl
, isRequiredBaseMethod
) {
416 const CXXMethodDecl
*Decl
= Node
.getCanonicalDecl();
417 return Decl
&& hasCustomAttribute
<moz_required_base_method
>(Decl
);
420 AST_MATCHER(CXXMethodDecl
, isNonVirtual
) {
421 const CXXMethodDecl
*Decl
= Node
.getCanonicalDecl();
422 return Decl
&& !Decl
->isVirtual();
425 AST_MATCHER(FunctionDecl
, isMozMustReturnFromCaller
) {
426 const FunctionDecl
*Decl
= Node
.getCanonicalDecl();
428 hasCustomAttribute
<moz_must_return_from_caller_if_this_is_arg
>(Decl
);
431 AST_MATCHER(FunctionDecl
, isMozTemporaryLifetimeBound
) {
432 const FunctionDecl
*Decl
= Node
.getCanonicalDecl();
433 return Decl
&& hasCustomAttribute
<moz_lifetime_bound
>(Decl
);
436 /// This matcher will select default args which have nullptr as the value.
437 AST_MATCHER(CXXDefaultArgExpr
, isNullDefaultArg
) {
438 const Expr
*Expr
= Node
.getExpr();
439 return Expr
&& Expr
->isNullPointerConstant(Finder
->getASTContext(),
440 Expr::NPC_NeverValueDependent
);
443 AST_MATCHER(UsingDirectiveDecl
, isUsingNamespaceMozillaJava
) {
444 const NamespaceDecl
*Namespace
= Node
.getNominatedNamespace();
445 const std::string
&FQName
= Namespace
->getQualifiedNameAsString();
447 static const char NAMESPACE
[] = "mozilla::java";
448 static const char PREFIX
[] = "mozilla::java::";
450 // We match both the `mozilla::java` namespace itself as well as any other
451 // namespaces contained within the `mozilla::java` namespace.
452 return !FQName
.compare(NAMESPACE
) ||
453 !FQName
.compare(0, sizeof(PREFIX
) - 1, PREFIX
);
456 AST_MATCHER(MemberExpr
, hasKnownLiveAnnotation
) {
457 ValueDecl
*Member
= Node
.getMemberDecl();
458 FieldDecl
*Field
= dyn_cast
<FieldDecl
>(Member
);
459 return Field
&& hasCustomAttribute
<moz_known_live
>(Field
);
462 #define GENERATE_JSTYPEDEF_PAIR(templateName) \
463 {templateName "Function", templateName "<JSFunction*>"}, \
464 {templateName "Id", templateName "<JS::PropertyKey>"}, \
465 {templateName "Object", templateName "<JSObject*>"}, \
466 {templateName "Script", templateName "<JSScript*>"}, \
467 {templateName "String", templateName "<JSString*>"}, \
468 {templateName "Symbol", templateName "<JS::Symbol*>"}, \
469 {templateName "BigInt", templateName "<JS::BigInt*>"}, \
470 {templateName "Value", templateName "<JS::Value>"}, \
471 {templateName "ValueVector", templateName "Vector<JS::Value>"}, \
472 {templateName "ObjectVector", templateName "Vector<JSObject*>"}, { \
473 templateName "IdVector", templateName "Vector<JS::PropertyKey>" \
476 static const char *const JSHandleRootedTypedefMap
[][2] = {
477 GENERATE_JSTYPEDEF_PAIR("JS::Handle"),
478 GENERATE_JSTYPEDEF_PAIR("JS::MutableHandle"),
479 GENERATE_JSTYPEDEF_PAIR("JS::Rooted"),
480 // Technically there is no PersistentRootedValueVector, and that's okay
481 GENERATE_JSTYPEDEF_PAIR("JS::PersistentRooted"),
484 AST_MATCHER(DeclaratorDecl
, isUsingJSHandleRootedTypedef
) {
485 QualType Type
= Node
.getType();
486 std::string TypeName
= Type
.getAsString();
487 for (auto &pair
: JSHandleRootedTypedefMap
) {
488 if (!TypeName
.compare(pair
[0])) {
495 } // namespace ast_matchers