Bug 1487622 - Refactor the clang plugin wrt attributes r=andi
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 18 Sep 2018 13:03:33 +0000
changeset 437021 e3118f787e336e908dd7a8f42d71211cb8738bfe
parent 437020 303fb0953da3d720582705879415680d33270838
child 437022 b902b6f391e3a6b16f8815fa7140bb92d8dc57ff
push id34667
push useraiakab@mozilla.com
push dateWed, 19 Sep 2018 02:13:23 +0000
treeherdermozilla-central@3857cbe7b717 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersandi
bugs1487622
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1487622 - Refactor the clang plugin wrt attributes r=andi - We forcefully remove annotations from the AST so that they don't end up impacting codegen. - We change the API such that we use identifiers instead of strings, reducing the chances of typo errors. Differential Revision: https://phabricator.services.mozilla.com/D5493
build/clang-plugin/CanRunScriptChecker.cpp
build/clang-plugin/CustomAttributes.cpp
build/clang-plugin/CustomAttributes.h
build/clang-plugin/CustomAttributes.inc
build/clang-plugin/CustomMatchers.h
build/clang-plugin/CustomTypeAnnotation.cpp
build/clang-plugin/CustomTypeAnnotation.h
build/clang-plugin/ExplicitOperatorBoolChecker.cpp
build/clang-plugin/MemMoveAnnotation.h
build/clang-plugin/MustOverrideChecker.cpp
build/clang-plugin/MustReturnFromCallerChecker.cpp
build/clang-plugin/MustUseChecker.cpp
build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
build/clang-plugin/OverrideBaseCallChecker.cpp
build/clang-plugin/Utils.h
build/clang-plugin/moz.build
--- a/build/clang-plugin/CanRunScriptChecker.cpp
+++ b/build/clang-plugin/CanRunScriptChecker.cpp
@@ -105,17 +105,17 @@ private:
 
   std::unordered_set<const FunctionDecl *> &CanRunScriptFuncs;
 };
 
 void FuncSetCallback::run(const MatchFinder::MatchResult &Result) {
   const FunctionDecl *Func;
   if (auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
     Func = Lambda->getCallOperator();
-    if (!Func || !hasCustomAnnotation(Func, "moz_can_run_script"))
+    if (!Func || !hasCustomAttribute<moz_can_run_script>(Func))
       return;
   } else {
     Func = Result.Nodes.getNodeAs<FunctionDecl>("canRunScriptFunction");
   }
 
   CanRunScriptFuncs.insert(Func);
 
   // If this is a method, we check the methods it overrides.
@@ -206,17 +206,17 @@ void CanRunScriptChecker::check(const Ma
   //
   // In addition, If the parent function is annotated as a
   // CAN_RUN_SCRIPT_BOUNDARY, we don't want to complain about it calling a
   // CAN_RUN_SCRIPT function. This is a mechanism to opt out of the infectious
   // nature of CAN_RUN_SCRIPT which is necessary in some tricky code like
   // Bindings.
   if (ParentFunction &&
       (CanRunScriptFuncs.count(ParentFunction) ||
-       hasCustomAnnotation(ParentFunction, "moz_can_run_script_boundary"))) {
+       hasCustomAttribute<moz_can_run_script_boundary>(ParentFunction))) {
     ParentFunction = nullptr;
   }
 
   // Get the call range from either the CallExpr or the ConstructExpr.
   SourceRange CallRange;
   if (Call) {
     CallRange = Call->getSourceRange();
   } else if (Construct) {
@@ -231,17 +231,17 @@ void CanRunScriptChecker::check(const Ma
   if (InvalidArg) {
     diag(InvalidArg->getExprLoc(), ErrorInvalidArg, DiagnosticIDs::Error)
         << CallRange;
   }
 
   // If the parent function is not marked as MOZ_CAN_RUN_SCRIPT, we emit an
   // error and a not indicating it.
   if (ParentFunction) {
-    assert(!hasCustomAnnotation(ParentFunction, "moz_can_run_script") &&
+    assert(!hasCustomAttribute<moz_can_run_script>(ParentFunction) &&
            "Matcher missed something");
 
     diag(CallRange.getBegin(), ErrorNonCanRunScriptParent, DiagnosticIDs::Error)
         << CallRange;
 
     diag(ParentFunction->getLocation(), NoteNonCanRunScriptParent,
          DiagnosticIDs::Note);
   }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/CustomAttributes.cpp
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+#include "CustomAttributes.h"
+#include "plugin.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+
+/* Having annotations in the AST unexpectedly impacts codegen.
+ * Ideally, we'd avoid having annotations at all, by using an API such as
+ * the one from https://reviews.llvm.org/D31338, and storing the attributes
+ * data separately from the AST on our own. Unfortunately, there is no such
+ * API currently in clang, so we must do without.
+ * We can do something similar, though, where we go through the AST before
+ * running the checks, create a mapping of AST nodes to attributes, and
+ * remove the attributes/annotations from the AST nodes.
+ * Not all declarations can be reached from the decl() AST matcher, though,
+ * so we do our best effort (getting the other declarations we look at in
+ * checks). We emit a warning when checks look at a note that still has
+ * annotations attached (aka, hasn't been seen during our first pass),
+ * so that those don't go unnoticed. (-Werror should then take care of
+ * making that an error)
+ */
+
+using namespace clang;
+using namespace llvm;
+
+static DenseMap<const Decl*, CustomAttributesSet> AttributesCache;
+
+static CustomAttributesSet CacheAttributes(const Decl* D)
+{
+  CustomAttributesSet attrs = {};
+  for (auto Attr : D->specific_attrs<AnnotateAttr>()) {
+    auto annotation = Attr->getAnnotation();
+#define ATTR(a) \
+    if (annotation == #a) { \
+      attrs.has_ ## a = true; \
+    } else
+#include "CustomAttributes.inc"
+#undef ATTR
+    {}
+  }
+  const_cast<Decl*>(D)->dropAttr<AnnotateAttr>();
+  AttributesCache.insert(std::make_pair(D, attrs));
+  return attrs;
+}
+
+static void Report(const Decl* D, const char* message)
+{
+  ASTContext& Context = D->getASTContext();
+  DiagnosticsEngine& Diag = Context.getDiagnostics();
+  unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Warning, message);
+  Diag.Report(D->getLocStart(), ID);
+}
+
+CustomAttributesSet GetAttributes(const Decl* D)
+{
+  CustomAttributesSet attrs = {};
+  if (D->hasAttr<AnnotateAttr>()) {
+    Report(D, "Declaration has unhandled annotations.");
+    attrs = CacheAttributes(D);
+  } else {
+    auto attributes = AttributesCache.find(D);
+    if (attributes != AttributesCache.end()) {
+      attrs = attributes->second;
+    }
+  }
+  return attrs;
+}
+
+bool hasCustomAttribute(const clang::Decl* D, CustomAttributes A)
+{
+  CustomAttributesSet attrs = GetAttributes(D);
+  switch (A) {
+#define ATTR(a) case a: return attrs.has_ ## a;
+#include "CustomAttributes.inc"
+#undef ATTR
+  }
+  return false;
+}
+
+class CustomAttributesMatcher : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  virtual void
+  run(const ast_matchers::MatchFinder::MatchResult &Result) final
+  {
+    if (auto D = Result.Nodes.getNodeAs<Decl>("decl")) {
+      CacheAttributes(D);
+    } else if (auto L = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
+      CacheAttributes(L->getCallOperator());
+      CacheAttributes(L->getLambdaClass());
+    }
+  }
+};
+
+class CustomAttributesAction : public PluginASTAction {
+public:
+  ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
+                                   StringRef FileName) override {
+    auto& Context = CI.getASTContext();
+    auto AstMatcher = new (Context.Allocate<MatchFinder>()) MatchFinder();
+    auto Matcher = new (Context.Allocate<CustomAttributesMatcher>()) CustomAttributesMatcher();
+    AstMatcher->addMatcher(decl().bind("decl"), Matcher);
+    AstMatcher->addMatcher(lambdaExpr().bind("lambda"), Matcher);
+    return AstMatcher->newASTConsumer();
+  }
+
+  bool ParseArgs(const CompilerInstance &CI,
+                 const std::vector<std::string> &Args) override {
+    return true;
+  }
+
+  ActionType getActionType() override {
+    return AddBeforeMainAction;
+  }
+};
+
+static FrontendPluginRegistry::Add<CustomAttributesAction> X(
+  "moz-custom-attributes",
+  "prepare custom attributes for moz-check");
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/CustomAttributes.h
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CustomAttributes_h__
+#define CustomAttributes_h__
+
+#include "clang/AST/DeclBase.h"
+#include "llvm/ADT/StringRef.h"
+
+enum CustomAttributes {
+#define ATTR(a) a,
+#include "CustomAttributes.inc"
+#undef ATTR
+};
+
+struct CustomAttributesSet {
+#define ATTR(a) bool has_ ## a: 1;
+#include "CustomAttributes.inc"
+#undef ATTR
+};
+
+template<CustomAttributes A>
+bool hasCustomAttribute(const clang::Decl* D) {
+  return false;
+}
+
+extern CustomAttributesSet GetAttributes(const clang::Decl* D);
+
+#define ATTR(name) \
+  template<> \
+  inline bool hasCustomAttribute<name>(const clang::Decl* D) { \
+    return GetAttributes(D).has_ ## name; \
+  }
+#include "CustomAttributes.inc"
+#undef ATTR
+
+extern bool hasCustomAttribute(const clang::Decl* D, CustomAttributes A);
+
+#endif /* CustomAttributes_h__ */
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/CustomAttributes.inc
@@ -0,0 +1,27 @@
+ATTR(moz_can_run_script)
+ATTR(moz_can_run_script_boundary)
+ATTR(moz_global_class)
+ATTR(moz_heap_allocator)
+ATTR(moz_heap_class)
+ATTR(moz_implicit)
+ATTR(moz_inherit_type_annotations_from_template_args)
+ATTR(moz_is_smartptr_to_refcounted)
+ATTR(moz_may_call_after_must_return)
+ATTR(moz_must_override)
+ATTR(moz_must_return_from_caller)
+ATTR(moz_must_use_type)
+ATTR(moz_needs_memmovable_members)
+ATTR(moz_needs_memmovable_type)
+ATTR(moz_needs_no_vtable_type)
+ATTR(moz_no_addref_release_on_return)
+ATTR(moz_no_arith_expr_in_arg)
+ATTR(moz_no_dangling_on_temporaries)
+ATTR(moz_non_autoable)
+ATTR(moz_non_memmovable)
+ATTR(moz_non_param)
+ATTR(moz_non_temporary_class)
+ATTR(moz_nonheap_class)
+ATTR(moz_required_base_method)
+ATTR(moz_stack_class)
+ATTR(moz_temporary_class)
+ATTR(moz_trivial_ctor_dtor)
--- a/build/clang-plugin/CustomMatchers.h
+++ b/build/clang-plugin/CustomMatchers.h
@@ -9,29 +9,29 @@
 #include "Utils.h"
 
 namespace clang {
 namespace ast_matchers {
 
 /// This matcher will match any function declaration that is declared as a heap
 /// allocator.
 AST_MATCHER(FunctionDecl, heapAllocator) {
-  return hasCustomAnnotation(&Node, "moz_heap_allocator");
+  return hasCustomAttribute<moz_heap_allocator>(&Node);
 }
 
 /// This matcher will match any declaration that is marked as not accepting
 /// arithmetic expressions in its arguments.
 AST_MATCHER(Decl, noArithmeticExprInArgs) {
-  return hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg");
+  return hasCustomAttribute<moz_no_arith_expr_in_arg>(&Node);
 }
 
 /// This matcher will match any C++ class that is marked as having a trivial
 /// constructor and destructor.
 AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
-  return hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
+  return hasCustomAttribute<moz_trivial_ctor_dtor>(&Node);
 }
 
 /// This matcher will match lvalue-ref-qualified methods.
 AST_MATCHER(CXXMethodDecl, isLValueRefQualified) {
   return Node.getRefQualifier() == RQ_LValue;
 }
 
 /// This matcher will match rvalue-ref-qualified methods.
@@ -51,29 +51,29 @@ AST_POLYMORPHIC_MATCHER(isFirstParty,
 AST_MATCHER(Expr, isTemporary) {
   return Node.isRValue() || Node.isXValue() ||
          isa<MaterializeTemporaryExpr>(&Node);
 }
 
 /// This matcher will match any method declaration that is marked as returning
 /// a pointer deleted by the destructor of the class.
 AST_MATCHER(CXXMethodDecl, noDanglingOnTemporaries) {
-  return hasCustomAnnotation(&Node, "moz_no_dangling_on_temporaries");
+  return hasCustomAttribute<moz_no_dangling_on_temporaries>(&Node);
 }
 
 /// This matcher will match any function declaration that is marked to prohibit
 /// calling AddRef or Release on its return value.
 AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
-  return hasCustomAnnotation(&Node, "moz_no_addref_release_on_return");
+  return hasCustomAttribute<moz_no_addref_release_on_return>(&Node);
 }
 
 /// This matcher will match any function declaration that is marked as being
 /// allowed to run script.
 AST_MATCHER(FunctionDecl, hasCanRunScriptAnnotation) {
-  return hasCustomAnnotation(&Node, "moz_can_run_script");
+  return hasCustomAttribute<moz_can_run_script>(&Node);
 }
 
 /// This matcher will match all arithmetic binary operators.
 AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
   BinaryOperatorKind OpCode = Node.getOpcode();
   return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem ||
          OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl ||
          OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor ||
@@ -151,32 +151,32 @@ AST_MATCHER(CXXRecordDecl, hasRefCntMemb
 }
 
 /// This matcher will select classes which are refcounted.
 AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
 
 AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
 
 AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
-  return hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
+  return hasCustomAttribute<moz_needs_no_vtable_type>(&Node);
 }
 
 /// This matcher will select classes which are non-memmovable
 AST_MATCHER(QualType, isNonMemMovable) {
   return NonMemMovable.hasEffectiveAnnotation(Node);
 }
 
 /// This matcher will select classes which require a memmovable template arg
 AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
-  return hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
+  return hasCustomAttribute<moz_needs_memmovable_type>(&Node);
 }
 
 /// This matcher will select classes which require all members to be memmovable
 AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
-  return hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
+  return hasCustomAttribute<moz_needs_memmovable_members>(&Node);
 }
 
 AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
   const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
   return
       // Skip constructors in system headers
       !ASTIsInSystemHeader(Declaration->getASTContext(), *Declaration) &&
       // Skip ignored namespaces and paths
@@ -193,25 +193,25 @@ AST_MATCHER(CXXConstructorDecl, isIntere
 
 AST_MATCHER_P(Expr, ignoreTrivials, internal::Matcher<Expr>, InnerMatcher) {
   return InnerMatcher.matches(*IgnoreTrivials(&Node), Finder, Builder);
 }
 
 // We can't call this "isImplicit" since it clashes with an existing matcher in
 // clang.
 AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) {
-  return hasCustomAnnotation(&Node, "moz_implicit");
+  return hasCustomAttribute<moz_implicit>(&Node);
 }
 
 AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
 
 AST_MATCHER(QualType, autoNonAutoableType) {
   if (const AutoType *T = Node->getContainedAutoType()) {
     if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) {
-      return hasCustomAnnotation(Rec, "moz_non_autoable");
+      return hasCustomAttribute<moz_non_autoable>(Rec);
     }
   }
   return false;
 }
 
 AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
   return Node.isExplicit() && Node.isMoveConstructor();
 }
@@ -253,39 +253,39 @@ AST_MATCHER(QualType, isRefPtr) { return
 AST_MATCHER(QualType, isSmartPtrToRefCounted) {
   auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
   if (!D) {
     return false;
   }
 
   D = D->getCanonicalDecl();
 
-  return D && hasCustomAnnotation(D, "moz_is_smartptr_to_refcounted");
+  return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
 }
 
 AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
   const CXXRecordDecl *Decl = Node.getCanonicalDecl();
 
   // Must have definition and should inherit other classes
   return Decl && Decl->hasDefinition() && Decl->getNumBases();
 }
 
 AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
   const CXXMethodDecl *Decl = Node.getCanonicalDecl();
-  return Decl && hasCustomAnnotation(Decl, "moz_required_base_method");
+  return Decl && hasCustomAttribute<moz_required_base_method>(Decl);
 }
 
 AST_MATCHER(CXXMethodDecl, isNonVirtual) {
   const CXXMethodDecl *Decl = Node.getCanonicalDecl();
   return Decl && !Decl->isVirtual();
 }
 
 AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
   const FunctionDecl *Decl = Node.getCanonicalDecl();
-  return Decl && hasCustomAnnotation(Decl, "moz_must_return_from_caller");
+  return Decl && hasCustomAttribute<moz_must_return_from_caller>(Decl);
 }
 
 #if CLANG_VERSION_FULL < 309
 /// DISCLAIMER: This is a copy/paste from the Clang source code starting from
 /// Clang 3.9, so that this matcher is supported in lower versions.
 ///
 /// \brief Matches declaration of the function the statement belongs to
 ///
--- a/build/clang-plugin/CustomTypeAnnotation.cpp
+++ b/build/clang-plugin/CustomTypeAnnotation.cpp
@@ -1,26 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CustomTypeAnnotation.h"
 #include "Utils.h"
 
 CustomTypeAnnotation StackClass =
-    CustomTypeAnnotation("moz_stack_class", "stack");
+    CustomTypeAnnotation(moz_stack_class, "stack");
 CustomTypeAnnotation GlobalClass =
-    CustomTypeAnnotation("moz_global_class", "global");
+    CustomTypeAnnotation(moz_global_class, "global");
 CustomTypeAnnotation NonHeapClass =
-    CustomTypeAnnotation("moz_nonheap_class", "non-heap");
-CustomTypeAnnotation HeapClass = CustomTypeAnnotation("moz_heap_class", "heap");
+    CustomTypeAnnotation(moz_nonheap_class, "non-heap");
+CustomTypeAnnotation HeapClass = CustomTypeAnnotation(moz_heap_class, "heap");
 CustomTypeAnnotation NonTemporaryClass =
-    CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
+    CustomTypeAnnotation(moz_non_temporary_class, "non-temporary");
 CustomTypeAnnotation TemporaryClass =
-    CustomTypeAnnotation("moz_temporary_class", "temporary");
+    CustomTypeAnnotation(moz_temporary_class, "temporary");
 
 void CustomTypeAnnotation::dumpAnnotationReason(BaseCheck &Check, QualType T,
                                                 SourceLocation Loc) {
   const char *Inherits =
       "%1 is a %0 type because it inherits from a %0 type %2";
   const char *Member = "%1 is a %0 type because member %2 is a %0 type %3";
   const char *Array = "%1 is a %0 type because it is an array of %0 type %2";
   const char *Templ =
@@ -69,17 +69,17 @@ void CustomTypeAnnotation::dumpAnnotatio
     T = Reason.Type;
     Reason = directAnnotationReason(T);
   }
 }
 
 CustomTypeAnnotation::AnnotationReason
 CustomTypeAnnotation::directAnnotationReason(QualType T) {
   if (const TagDecl *D = T->getAsTagDecl()) {
-    if (hasCustomAnnotation(D, Spelling)) {
+    if (hasCustomAttribute(D, Attribute)) {
       AnnotationReason Reason = {T, RK_Direct, nullptr, ""};
       return Reason;
     }
 
     std::string ImplAnnotReason = getImplicitReason(D);
     if (!ImplAnnotReason.empty()) {
       AnnotationReason Reason = {T, RK_Implicit, nullptr, ImplAnnotReason};
       return Reason;
@@ -122,18 +122,18 @@ CustomTypeAnnotation::directAnnotationRe
           AnnotationReason Reason = {Field->getType(), RK_Field, Field, ""};
           Cache[Key] = Reason;
           return Reason;
         }
       }
 
       // Recurse into template arguments if the annotation
       // MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present
-      if (hasCustomAnnotation(
-              Declaration, "moz_inherit_type_annotations_from_template_args")) {
+      if (hasCustomAttribute<moz_inherit_type_annotations_from_template_args>(
+              Declaration)) {
         const ClassTemplateSpecializationDecl *Spec =
             dyn_cast<ClassTemplateSpecializationDecl>(Declaration);
         if (Spec) {
           const TemplateArgumentList &Args = Spec->getTemplateArgs();
 
           AnnotationReason Reason = tmplArgAnnotationReason(Args.asArray());
           if (Reason.Kind != RK_None) {
             Cache[Key] = Reason;
--- a/build/clang-plugin/CustomTypeAnnotation.h
+++ b/build/clang-plugin/CustomTypeAnnotation.h
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CustomTypeAnnotation_h__
 #define CustomTypeAnnotation_h__
 
+#include "CustomAttributes.h"
 #include "plugin.h"
 
 class CustomTypeAnnotation {
   enum ReasonKind {
     RK_None,
     RK_Direct,
     RK_ArrayElement,
     RK_BaseClass,
@@ -22,23 +23,23 @@ class CustomTypeAnnotation {
     ReasonKind Kind;
     const FieldDecl *Field;
     std::string ImplicitReason;
 
     bool valid() const { return Kind != RK_None; }
   };
   typedef DenseMap<void *, AnnotationReason> ReasonCache;
 
-  const char *Spelling;
+  CustomAttributes Attribute;
   const char *Pretty;
   ReasonCache Cache;
 
 public:
-  CustomTypeAnnotation(const char *Spelling, const char *Pretty)
-      : Spelling(Spelling), Pretty(Pretty){};
+  CustomTypeAnnotation(CustomAttributes Attribute, const char *Pretty)
+      : Attribute(Attribute), Pretty(Pretty){};
 
   virtual ~CustomTypeAnnotation() {}
 
   // Checks if this custom annotation "effectively affects" the given type.
   bool hasEffectiveAnnotation(QualType T) {
     return directAnnotationReason(T).valid();
   }
   void dumpAnnotationReason(BaseCheck &Check, QualType T, SourceLocation Loc);
--- a/build/clang-plugin/ExplicitOperatorBoolChecker.cpp
+++ b/build/clang-plugin/ExplicitOperatorBoolChecker.cpp
@@ -17,17 +17,17 @@ void ExplicitOperatorBoolChecker::regist
 
 void ExplicitOperatorBoolChecker::check(
     const MatchFinder::MatchResult &Result) {
   const CXXConversionDecl *Method =
       Result.Nodes.getNodeAs<CXXConversionDecl>("node");
   const CXXRecordDecl *Clazz = Method->getParent();
 
   if (!Method->isExplicitSpecified() &&
-      !hasCustomAnnotation(Method, "moz_implicit") &&
+      !hasCustomAttribute<moz_implicit>(Method) &&
       !ASTIsInSystemHeader(Method->getASTContext(), *Method) &&
       isInterestingDeclForImplicitConversion(Method)) {
     diag(Method->getLocStart(), "bad implicit conversion operator for %0",
          DiagnosticIDs::Error)
         << Clazz;
     diag(Method->getLocStart(), "consider adding the explicit keyword to %0",
          DiagnosticIDs::Note)
         << "'operator bool'";
--- a/build/clang-plugin/MemMoveAnnotation.h
+++ b/build/clang-plugin/MemMoveAnnotation.h
@@ -7,17 +7,17 @@
 
 #include "CustomMatchers.h"
 #include "CustomTypeAnnotation.h"
 #include "Utils.h"
 
 class MemMoveAnnotation final : public CustomTypeAnnotation {
 public:
   MemMoveAnnotation()
-      : CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {}
+      : CustomTypeAnnotation(moz_non_memmovable, "non-memmove()able") {}
 
   virtual ~MemMoveAnnotation() {}
 
 protected:
   std::string getImplicitReason(const TagDecl *D) const override {
     // Annotate everything in ::std, with a few exceptions; see bug
     // 1201314 for discussion.
     if (getDeclarationNamespace(D) == "std") {
--- a/build/clang-plugin/MustOverrideChecker.cpp
+++ b/build/clang-plugin/MustOverrideChecker.cpp
@@ -29,17 +29,17 @@ void MustOverrideChecker::check(const Ma
     // do any checking here. For complete correctness, we should visit
     // template instantiations, but this case is likely to be rare, so we will
     // ignore it until it becomes important.
     if (!Parent) {
       continue;
     }
     Parent = Parent->getDefinition();
     for (const auto &M : Parent->methods()) {
-      if (hasCustomAnnotation(M, "moz_must_override"))
+      if (hasCustomAttribute<moz_must_override>(M))
         MustOverrides.push_back(M);
     }
   }
 
   for (auto &O : MustOverrides) {
     bool Overridden = false;
     for (const auto &M : D->methods()) {
       // The way that Clang checks if a method M overrides its parent method
--- a/build/clang-plugin/MustReturnFromCallerChecker.cpp
+++ b/build/clang-plugin/MustReturnFromCallerChecker.cpp
@@ -79,17 +79,17 @@ bool MustReturnFromCallerChecker::immedi
     }
 
     // It's also OK to call any function or method which is annotated with
     // MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls
     // to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
     if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
       auto Callee = CE->getDirectCallee();
       if (Callee &&
-          hasCustomAnnotation(Callee, "moz_may_call_after_must_return")) {
+          hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
         continue;
       }
 
       if (Callee && isa<CXXConversionDecl>(Callee)) {
         continue;
       }
     }
 
--- a/build/clang-plugin/MustUseChecker.cpp
+++ b/build/clang-plugin/MustUseChecker.cpp
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MustUseChecker.h"
 #include "CustomMatchers.h"
 #include "CustomTypeAnnotation.h"
 
 CustomTypeAnnotation MustUse =
-    CustomTypeAnnotation("moz_must_use_type", "must-use");
+    CustomTypeAnnotation(moz_must_use_type, "must-use");
 
 void MustUseChecker::registerMatchers(MatchFinder *AstMatcher) {
   AstMatcher->addMatcher(switchCase().bind("switchcase"), this);
   AstMatcher->addMatcher(compoundStmt().bind("compound"), this);
   AstMatcher->addMatcher(ifStmt().bind("if"), this);
   AstMatcher->addMatcher(whileStmt().bind("while"), this);
   AstMatcher->addMatcher(doStmt().bind("do"), this);
   AstMatcher->addMatcher(forStmt().bind("for"), this);
--- a/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
+++ b/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
@@ -16,17 +16,17 @@ void NoAddRefReleaseOnReturnChecker::che
     const MatchFinder::MatchResult &Result) {
   const MemberExpr *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
   const Expr *Base = IgnoreTrivials(Member->getBase());
 
   // Check if the call to AddRef() or Release() was made on the result of a call
   // to a MOZ_NO_ADDREF_RELEASE_ON_RETURN function or method.
   if (auto *Call = dyn_cast<CallExpr>(Base)) {
     if (auto *Callee = Call->getDirectCallee()) {
-      if (hasCustomAnnotation(Callee, "moz_no_addref_release_on_return")) {
+      if (hasCustomAttribute<moz_no_addref_release_on_return>(Callee)) {
         diag(Call->getLocStart(),
              "%1 cannot be called on the return value of %0",
              DiagnosticIDs::Error)
             << Callee << dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
       }
     }
   }
 }
--- a/build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
+++ b/build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NonParamInsideFunctionDeclChecker.h"
 #include "CustomMatchers.h"
 
 class NonParamAnnotation : public CustomTypeAnnotation {
 public:
-  NonParamAnnotation() : CustomTypeAnnotation("moz_non_param", "non-param"){};
+  NonParamAnnotation() : CustomTypeAnnotation(moz_non_param, "non-param"){};
 
 protected:
   // Adding alignas(_) on a struct implicitly marks it as MOZ_NON_PARAM, due to
   // MSVC limitations which prevent passing explcitly aligned types by value as
   // parameters. This overload of hasFakeAnnotation injects fake MOZ_NON_PARAM
   // annotations onto these types.
   std::string getImplicitReason(const TagDecl *D) const override {
     // Check if the decl itself has an AlignedAttr on it.
--- a/build/clang-plugin/OverrideBaseCallChecker.cpp
+++ b/build/clang-plugin/OverrideBaseCallChecker.cpp
@@ -6,17 +6,17 @@
 #include "CustomMatchers.h"
 
 void OverrideBaseCallChecker::registerMatchers(MatchFinder *AstMatcher) {
   AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), this);
 }
 
 bool OverrideBaseCallChecker::isRequiredBaseMethod(
     const CXXMethodDecl *Method) {
-  return hasCustomAnnotation(Method, "moz_required_base_method");
+  return hasCustomAttribute<moz_required_base_method>(Method);
 }
 
 void OverrideBaseCallChecker::evaluateExpression(
     const Stmt *StmtExpr, std::list<const CXXMethodDecl *> &MethodList) {
   // Continue while we have methods in our list
   if (!MethodList.size()) {
     return;
   }
--- a/build/clang-plugin/Utils.h
+++ b/build/clang-plugin/Utils.h
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef Utils_h__
 #define Utils_h__
 
+#include "CustomAttributes.h"
 #include "ThirdPartyPaths.h"
 #include "plugin.h"
 
 // Check if the given expression contains an assignment expression.
 // This can either take the form of a Binary Operator or a
 // Overloaded Operator Call.
 inline bool hasSideEffectAssignment(const Expr *Expression) {
   if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
@@ -328,35 +329,22 @@ inline const FieldDecl *getBaseRefCntMem
 
 inline const FieldDecl *getBaseRefCntMember(QualType T) {
   while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
     T = ArrTy->getElementType();
   CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
   return Clazz ? getBaseRefCntMember(Clazz) : 0;
 }
 
-inline bool hasCustomAnnotation(const Decl *D, StringRef Spelling) {
-  iterator_range<specific_attr_iterator<AnnotateAttr>> Attrs =
-      D->specific_attrs<AnnotateAttr>();
-
-  for (AnnotateAttr *Attr : Attrs) {
-    if (Attr->getAnnotation() == Spelling) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 inline bool isPlacementNew(const CXXNewExpr *Expression) {
   // Regular new expressions aren't placement new
   if (Expression->getNumPlacementArgs() == 0)
     return false;
   const FunctionDecl *Declaration = Expression->getOperatorNew();
-  if (Declaration && hasCustomAnnotation(Declaration, "moz_heap_allocator")) {
+  if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) {
     return false;
   }
   return true;
 }
 
 inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) {
   SmallString<1024> FileName = SM.getFilename(Loc);
   llvm::sys::fs::make_absolute(FileName);
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -7,16 +7,17 @@
 HostSharedLibrary('clang-plugin')
 
 HOST_SOURCES += ['!ThirdPartyPaths.cpp']
 
 HOST_SOURCES += [
     'ArithmeticArgChecker.cpp',
     'AssertAssignmentChecker.cpp',
     'CanRunScriptChecker.cpp',
+    'CustomAttributes.cpp',
     'CustomTypeAnnotation.cpp',
     'DanglingOnTemporaryChecker.cpp',
     'DiagnosticsMatcher.cpp',
     'ExplicitImplicitChecker.cpp',
     'ExplicitOperatorBoolChecker.cpp',
     'KungFuDeathGripChecker.cpp',
     'MozCheckAction.cpp',
     'MustOverrideChecker.cpp',