Bug 1814798 - pt 1. Add bool to enable/disable PHC at runtime r=glandium
[gecko.git] / build / clang-plugin / CustomMatchers.h
blobece717e79918257cdcb78c2b2847a4107bc68cca
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"
9 #include "Utils.h"
11 #if CLANG_VERSION_FULL >= 1300
12 // Starting with clang-13 Expr::isRValue has been renamed to Expr::isPRValue
13 #define isRValue isPRValue
14 #endif
16 namespace clang {
17 namespace ast_matchers {
19 /// This matcher will match any function declaration that is declared as a heap
20 /// allocator.
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
38 /// destructor.
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
66 // SpiderMonkey.
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 ||
118 OpCode == UO_Not;
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();
155 StringRef FileName =
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();
167 StringRef FileName =
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)) {
171 return true;
175 return false;
178 AST_MATCHER(CallExpr, isInWhiteListForPrincipalGetUri) {
179 const auto Whitelist = {"nsIPrincipal.h", "BasePrincipal.cpp",
180 "ContentPrincipal.cpp"};
181 SourceLocation Loc = Node.getBeginLoc();
182 StringRef Filename =
183 getFilename(Finder->getASTContext().getSourceManager(), Loc);
185 for (auto Exclusion : Whitelist) {
186 if (Filename.find(Exclusion) != std::string::npos) {
187 return true;
190 return false;
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();
199 StringRef FileName =
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
206 // new threads.
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)) {
214 return false;
217 return true;
218 }();
219 if (match) {
220 return true;
224 // Check the first arg (the name of the thread).
225 const StringLiteral *nameArg =
226 dyn_cast<StringLiteral>(Node.getArg(0)->IgnoreImplicit());
227 if (nameArg) {
228 const StringRef name = nameArg->getString();
229 for (auto thread_name : allow_thread_names) {
230 if (name.equals(thread_name)) {
231 return true;
236 return false;
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);
243 if (Method) {
244 const auto &Name = getNameChecked(Method);
245 return Name == "AddRef" || Name == "Release";
247 return false;
250 /// This matcher will select classes which are refcounted AND have an mRefCnt
251 /// member.
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();
282 return
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
291 // implicit
292 !Declaration->isCopyOrMoveConstructor() &&
293 // We don't want inheriting constructors, since using declarations can't
294 // have attributes
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;
316 while (true) {
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) {
322 return false;
324 node = newNode;
328 // We can't call this "isImplicit" since it clashes with an existing matcher in
329 // clang.
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);
342 return false;
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)) {
367 return false;
370 StringRef Name = getNameChecked(Func);
371 if (Name != Snprintf && Name != Vsnprintf) {
372 return false;
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);
387 if (!D) {
388 return false;
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());
399 if (!D) {
400 return false;
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();
427 return Decl &&
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])) {
489 return true;
492 return false;
495 } // namespace ast_matchers
496 } // namespace clang
498 #undef isRValue
499 #endif