Bug 1324315 - Refactor the clang plugin to use a framework similar to clang-tidy's; r=mystor
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 17 Dec 2016 21:14:37 -0500
changeset 327508 8222951c9369ca13c6358bc32c9d5da6c6c2289b
parent 327507 ddfb48730883391cb38347d34917deaa978b9bc1
child 327509 409bdaed6b9f41f001db03eb5a5469e5ad5685e5
push id31135
push userkwierso@gmail.com
push dateThu, 29 Dec 2016 20:04:00 +0000
treeherdermozilla-central@79ef93672445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmystor
bugs1324315
milestone53.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 1324315 - Refactor the clang plugin to use a framework similar to clang-tidy's; r=mystor
build/clang-plugin/ArithmeticArgChecker.cpp
build/clang-plugin/ArithmeticArgChecker.h
build/clang-plugin/AssertAssignmentChecker.cpp
build/clang-plugin/AssertAssignmentChecker.h
build/clang-plugin/BaseCheck.h
build/clang-plugin/Checks.inc
build/clang-plugin/ChecksIncludes.inc
build/clang-plugin/CustomTypeAnnotation.cpp
build/clang-plugin/CustomTypeAnnotation.h
build/clang-plugin/DiagnosticsMatcher.cpp
build/clang-plugin/DiagnosticsMatcher.h
build/clang-plugin/ExplicitImplicitChecker.cpp
build/clang-plugin/ExplicitImplicitChecker.h
build/clang-plugin/ExplicitOperatorBoolChecker.cpp
build/clang-plugin/ExplicitOperatorBoolChecker.h
build/clang-plugin/KungFuDeathGripChecker.cpp
build/clang-plugin/KungFuDeathGripChecker.h
build/clang-plugin/MozillaTidyModule.cpp
build/clang-plugin/NaNExprChecker.cpp
build/clang-plugin/NaNExprChecker.h
build/clang-plugin/NeedsNoVTableTypeChecker.cpp
build/clang-plugin/NeedsNoVTableTypeChecker.h
build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
build/clang-plugin/NoAddRefReleaseOnReturnChecker.h
build/clang-plugin/NoAutoTypeChecker.cpp
build/clang-plugin/NoAutoTypeChecker.h
build/clang-plugin/NoDuplicateRefCntMemberChecker.cpp
build/clang-plugin/NoDuplicateRefCntMemberChecker.h
build/clang-plugin/NoExplicitMoveConstructorChecker.cpp
build/clang-plugin/NoExplicitMoveConstructorChecker.h
build/clang-plugin/NonMemMovableMemberChecker.cpp
build/clang-plugin/NonMemMovableMemberChecker.h
build/clang-plugin/NonMemMovableTemplateArgChecker.cpp
build/clang-plugin/NonMemMovableTemplateArgChecker.h
build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
build/clang-plugin/NonParamInsideFunctionDeclChecker.h
build/clang-plugin/OverrideBaseCallChecker.cpp
build/clang-plugin/OverrideBaseCallChecker.h
build/clang-plugin/OverrideBaseCallUsageChecker.cpp
build/clang-plugin/OverrideBaseCallUsageChecker.h
build/clang-plugin/RefCountedCopyConstructorChecker.cpp
build/clang-plugin/RefCountedCopyConstructorChecker.h
build/clang-plugin/RefCountedInsideLambdaChecker.cpp
build/clang-plugin/RefCountedInsideLambdaChecker.h
build/clang-plugin/ScopeChecker.cpp
build/clang-plugin/ScopeChecker.h
build/clang-plugin/SprintfLiteralChecker.cpp
build/clang-plugin/SprintfLiteralChecker.h
build/clang-plugin/TrivialCtorDtorChecker.cpp
build/clang-plugin/TrivialCtorDtorChecker.h
build/clang-plugin/import_mozilla_checks.py
build/clang-plugin/plugin.h
--- a/build/clang-plugin/ArithmeticArgChecker.cpp
+++ b/build/clang-plugin/ArithmeticArgChecker.cpp
@@ -1,17 +1,17 @@
 /* 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 "ArithmeticArgChecker.h"
 #include "CustomMatchers.h"
 
-void ArithmeticArgChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void ArithmeticArgChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
                      anyOf(hasDescendant(
                                binaryOperator(
                                    allOf(binaryArithmeticOperator(),
                                          hasLHS(hasDescendant(declRefExpr())),
                                          hasRHS(hasDescendant(declRefExpr()))))
                                    .bind("node")),
                            hasDescendant(
@@ -19,17 +19,17 @@ void ArithmeticArgChecker::registerMatch
                                    allOf(unaryArithmeticOperator(),
                                          hasUnaryOperand(allOf(
                                              hasType(builtinType()),
                                              anyOf(hasDescendant(declRefExpr()),
                                                    declRefExpr())))))
                                    .bind("node")))))
           .bind("call"),
       this);
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       cxxConstructExpr(
           allOf(hasDeclaration(noArithmeticExprInArgs()),
                 anyOf(hasDescendant(
                           binaryOperator(
                               allOf(binaryArithmeticOperator(),
                                     hasLHS(hasDescendant(declRefExpr())),
                                     hasRHS(hasDescendant(declRefExpr()))))
                               .bind("node")),
@@ -40,22 +40,19 @@ void ArithmeticArgChecker::registerMatch
                                         hasType(builtinType()),
                                         anyOf(hasDescendant(declRefExpr()),
                                               declRefExpr())))))
                               .bind("node")))))
           .bind("call"),
       this);
 }
 
-void ArithmeticArgChecker::run(
+void ArithmeticArgChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "cannot pass an arithmetic expression of built-in types to %0");
+  const char* Error = "cannot pass an arithmetic expression of built-in types to %0";
   const Expr *Expression = Result.Nodes.getNodeAs<Expr>("node");
   if (const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
-    Diag.Report(Expression->getLocStart(), ErrorID) << Call->getDirectCallee();
+    diag(Expression->getLocStart(), Error, DiagnosticIDs::Error) << Call->getDirectCallee();
   } else if (const CXXConstructExpr *Ctr =
                  Result.Nodes.getNodeAs<CXXConstructExpr>("call")) {
-    Diag.Report(Expression->getLocStart(), ErrorID) << Ctr->getConstructor();
+    diag(Expression->getLocStart(), Error, DiagnosticIDs::Error) << Ctr->getConstructor();
   }
 }
--- a/build/clang-plugin/ArithmeticArgChecker.h
+++ b/build/clang-plugin/ArithmeticArgChecker.h
@@ -2,15 +2,18 @@
  * 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 ArithmeticArgChecker_h__
 #define ArithmeticArgChecker_h__
 
 #include "plugin.h"
 
-class ArithmeticArgChecker : public MatchFinder::MatchCallback {
+class ArithmeticArgChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  ArithmeticArgChecker(StringRef CheckName,
+                       ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/AssertAssignmentChecker.cpp
+++ b/build/clang-plugin/AssertAssignmentChecker.cpp
@@ -1,24 +1,23 @@
 /* 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 "AssertAssignmentChecker.h"
 #include "CustomMatchers.h"
 
-void AssertAssignmentChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void AssertAssignmentChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       callExpr(isAssertAssignmentTestFunc()).bind("funcCall"),
       this);
 }
 
-void AssertAssignmentChecker::run(
+void AssertAssignmentChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned AssignInsteadOfComp = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "Forbidden assignment in assert expression");
   const CallExpr *FuncCall = Result.Nodes.getNodeAs<CallExpr>("funcCall");
 
   if (FuncCall && hasSideEffectAssignment(FuncCall)) {
-    Diag.Report(FuncCall->getLocStart(), AssignInsteadOfComp);
+    diag(FuncCall->getLocStart(),
+         "Forbidden assignment in assert expression",
+         DiagnosticIDs::Error);
   }
 }
--- a/build/clang-plugin/AssertAssignmentChecker.h
+++ b/build/clang-plugin/AssertAssignmentChecker.h
@@ -2,15 +2,18 @@
  * 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 AssertAssignmentChecker_h__
 #define AssertAssignmentChecker_h__
 
 #include "plugin.h"
 
-class AssertAssignmentChecker : public MatchFinder::MatchCallback {
+class AssertAssignmentChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  AssertAssignmentChecker(StringRef CheckName,
+                          ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/BaseCheck.h
@@ -0,0 +1,34 @@
+/* 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 BaseCheck_h__
+#define BaseCheck_h__
+
+class MozContext {};
+typedef MozContext ContextType;
+
+class BaseCheck : public MatchFinder::MatchCallback {
+public:
+  BaseCheck(StringRef CheckName, ContextType* Context) {}
+  virtual void registerMatchers(MatchFinder *Finder) {}
+  virtual void check(const MatchFinder::MatchResult &Result) {}
+  DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
+                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning) {
+    DiagnosticsEngine &Diag = Context->getDiagnostics();
+    unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(Level, Description);
+    // We treat all diagnostics as errors here.
+    return Diag.Report(Loc, ID);
+  }
+
+private:
+  void run(const MatchFinder::MatchResult &Result) override {
+    Context = Result.Context;
+    check(Result);
+  }
+
+private:
+  ASTContext* Context;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/Checks.inc
@@ -0,0 +1,27 @@
+/* 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/. */
+
+// The list of checker classes that are compatible with clang-tidy.
+
+CHECK(ArithmeticArgChecker, "arithmetic-argument")
+CHECK(AssertAssignmentChecker, "assignment-in-assert")
+CHECK(ExplicitImplicitChecker, "implicit-constructor")
+CHECK(ExplicitOperatorBoolChecker, "explicit-operator-bool")
+CHECK(KungFuDeathGripChecker, "kungfu-death-grip")
+CHECK(NaNExprChecker, "nan-expr")
+CHECK(NeedsNoVTableTypeChecker, "needs-no-vtable-type")
+CHECK(NoAddRefReleaseOnReturnChecker, "no-addref-release-on-return")
+CHECK(NoAutoTypeChecker, "no-auto-type")
+CHECK(NoDuplicateRefCntMemberChecker, "no-duplicate-refcnt-member")
+CHECK(NoExplicitMoveConstructorChecker, "no-explicit-move-constructor")
+CHECK(NonMemMovableMemberChecker, "non-memmovable-member")
+CHECK(NonMemMovableTemplateArgChecker, "non-memmovable-template-arg")
+CHECK(NonParamInsideFunctionDeclChecker, "non-memmovable-template-arg")
+CHECK(OverrideBaseCallChecker, "override-base-call")
+CHECK(OverrideBaseCallUsageChecker, "override-base-call-usage")
+CHECK(RefCountedCopyConstructorChecker, "refcounted-copy-constructor")
+CHECK(RefCountedInsideLambdaChecker, "refcounted-inside-lambda")
+CHECK(ScopeChecker, "scope")
+CHECK(SprintfLiteralChecker, "sprintf-literal")
+CHECK(TrivialCtorDtorChecker, "trivial-constructor-destructor")
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/ChecksIncludes.inc
@@ -0,0 +1,29 @@
+/* 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/. */
+
+// The list of #include directives necessary for the checker classes that
+// are compatible with clang-tidy.
+
+#include "ArithmeticArgChecker.h"
+#include "AssertAssignmentChecker.h"
+#include "ExplicitImplicitChecker.h"
+#include "ExplicitOperatorBoolChecker.h"
+#include "KungFuDeathGripChecker.h"
+#include "NaNExprChecker.h"
+#include "NeedsNoVTableTypeChecker.h"
+#include "NoAddRefReleaseOnReturnChecker.h"
+#include "NoAutoTypeChecker.h"
+#include "NoDuplicateRefCntMemberChecker.h"
+#include "NoExplicitMoveConstructorChecker.h"
+#include "NonMemMovableMemberChecker.h"
+#include "NonMemMovableTemplateArgChecker.h"
+#include "NonParamInsideFunctionDeclChecker.h"
+#include "OverrideBaseCallChecker.h"
+#include "OverrideBaseCallUsageChecker.h"
+#include "RefCountedCopyConstructorChecker.h"
+#include "RefCountedInsideLambdaChecker.h"
+#include "ScopeChecker.h"
+#include "SprintfLiteralChecker.h"
+#include "TrivialCtorDtorChecker.h"
+
--- a/build/clang-plugin/CustomTypeAnnotation.cpp
+++ b/build/clang-plugin/CustomTypeAnnotation.cpp
@@ -66,16 +66,64 @@ void CustomTypeAnnotation::dumpAnnotatio
       return;
     }
 
     T = Reason.Type;
     Reason = directAnnotationReason(T);
   }
 }
 
+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 =
+      "%1 is a %0 type because it has a template argument %0 type %2";
+
+  AnnotationReason Reason = directAnnotationReason(T);
+  for (;;) {
+    switch (Reason.Kind) {
+    case RK_ArrayElement:
+      Check.diag(Loc, Array, DiagnosticIDs::Note) << Pretty << T << Reason.Type;
+      break;
+    case RK_BaseClass: {
+      const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl();
+      assert(Declaration && "This type should be a C++ class");
+
+      Check.diag(Declaration->getLocation(), Inherits, DiagnosticIDs::Note)
+        << Pretty << T << Reason.Type;
+      break;
+    }
+    case RK_Field:
+      Check.diag(Reason.Field->getLocation(), Member, DiagnosticIDs::Note)
+          << Pretty << T << Reason.Field << Reason.Type;
+      break;
+    case RK_TemplateInherited: {
+      const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl();
+      assert(Declaration && "This type should be a C++ class");
+
+      Check.diag(Declaration->getLocation(), Templ, DiagnosticIDs::Note)
+        << Pretty << T << Reason.Type;
+      break;
+    }
+    default:
+      // FIXME (bug 1203263): note the original annotation.
+      return;
+    }
+
+    T = Reason.Type;
+    Reason = directAnnotationReason(T);
+  }
+}
+
 bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
 #if CLANG_VERSION_FULL >= 306
   if (const TagDecl *D = T->getAsTagDecl()) {
 #else
   if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) {
 #endif
     return hasFakeAnnotation(D) || MozChecker::hasCustomAnnotation(D, Spelling);
   }
--- a/build/clang-plugin/CustomTypeAnnotation.h
+++ b/build/clang-plugin/CustomTypeAnnotation.h
@@ -36,24 +36,26 @@ public:
   virtual ~CustomTypeAnnotation() {}
 
   // Checks if this custom annotation "effectively affects" the given type.
   bool hasEffectiveAnnotation(QualType T) {
     return directAnnotationReason(T).valid();
   }
   void dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T,
                             SourceLocation Loc);
+  void dumpAnnotationReason(BaseCheck &Check, QualType T,
+                            SourceLocation Loc);
 
-  void reportErrorIfPresent(DiagnosticsEngine &Diag, QualType T,
-                            SourceLocation Loc, unsigned ErrorID,
-                            unsigned NoteID) {
+  void reportErrorIfPresent(BaseCheck &Check, QualType T,
+                            SourceLocation Loc, const char* Error,
+                            const char* Note) {
     if (hasEffectiveAnnotation(T)) {
-      Diag.Report(Loc, ErrorID) << T;
-      Diag.Report(Loc, NoteID);
-      dumpAnnotationReason(Diag, T, Loc);
+      Check.diag(Loc, Error, DiagnosticIDs::Error) << T;
+      Check.diag(Loc, Note, DiagnosticIDs::Note);
+      dumpAnnotationReason(Check, T, Loc);
     }
   }
 
 private:
   bool hasLiteralAnnotation(QualType T) const;
   AnnotationReason directAnnotationReason(QualType T);
   AnnotationReason tmplArgAnnotationReason(ArrayRef<TemplateArgument> Args);
 
--- a/build/clang-plugin/DiagnosticsMatcher.cpp
+++ b/build/clang-plugin/DiagnosticsMatcher.cpp
@@ -1,29 +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/. */
 
 #include "DiagnosticsMatcher.h"
 
-DiagnosticsMatcher::DiagnosticsMatcher() {
-  Scope.registerMatcher(AstMatcher);
-  ArithmeticArg.registerMatcher(AstMatcher);
-  TrivialCtorDtor.registerMatcher(AstMatcher);
-  NaNExpr.registerMatcher(AstMatcher);
-  NoAddRefReleaseOnReturn.registerMatcher(AstMatcher);
-  RefCountedInsideLambda.registerMatcher(AstMatcher);
-  ExplicitOperatorBool.registerMatcher(AstMatcher);
-  NoDuplicateRefCntMember.registerMatcher(AstMatcher);
-  NeedsNoVTableType.registerMatcher(AstMatcher);
-  NonMemMovableTemplateArg.registerMatcher(AstMatcher);
-  NonMemMovableMember.registerMatcher(AstMatcher);
-  ExplicitImplicit.registerMatcher(AstMatcher);
-  NoAutoType.registerMatcher(AstMatcher);
-  NoExplicitMoveConstructor.registerMatcher(AstMatcher);
-  RefCountedCopyConstructor.registerMatcher(AstMatcher);
-  AssertAssignment.registerMatcher(AstMatcher);
-  KungFuDeathGrip.registerMatcher(AstMatcher);
-  SprintfLiteral.registerMatcher(AstMatcher);
-  OverrideBaseCall.registerMatcher(AstMatcher);
-  OverrideBaseCallUsage.registerMatcher(AstMatcher);
-  NonParamInsideFunctionDecl.registerMatcher(AstMatcher);
+DiagnosticsMatcher::DiagnosticsMatcher() :
+#define CHECK(cls, name) cls ## _(name),
+#include "Checks.inc"
+#undef CHECK
+  AstMatcher()
+{
+#define CHECK(cls, name) cls ## _.registerMatchers(&AstMatcher);
+#include "Checks.inc"
+#undef CHECK
 }
--- a/build/clang-plugin/DiagnosticsMatcher.h
+++ b/build/clang-plugin/DiagnosticsMatcher.h
@@ -1,61 +1,23 @@
 /* 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 DiagnosticsMatcher_h__
 #define DiagnosticsMatcher_h__
 
-#include "ArithmeticArgChecker.h"
-#include "AssertAssignmentChecker.h"
-#include "ExplicitImplicitChecker.h"
-#include "ExplicitOperatorBoolChecker.h"
-#include "KungFuDeathGripChecker.h"
-#include "NaNExprChecker.h"
-#include "NeedsNoVTableTypeChecker.h"
-#include "NoAddRefReleaseOnReturnChecker.h"
-#include "NoAutoTypeChecker.h"
-#include "NoDuplicateRefCntMemberChecker.h"
-#include "NoExplicitMoveConstructorChecker.h"
-#include "NonMemMovableMemberChecker.h"
-#include "NonMemMovableTemplateArgChecker.h"
-#include "NonParamInsideFunctionDeclChecker.h"
-#include "OverrideBaseCallChecker.h"
-#include "OverrideBaseCallUsageChecker.h"
-#include "RefCountedCopyConstructorChecker.h"
-#include "RefCountedInsideLambdaChecker.h"
-#include "ScopeChecker.h"
-#include "SprintfLiteralChecker.h"
-#include "TrivialCtorDtorChecker.h"
+#include "ChecksIncludes.inc"
 
 class DiagnosticsMatcher {
 public:
   DiagnosticsMatcher();
 
   ASTConsumerPtr makeASTConsumer() { return AstMatcher.newASTConsumer(); }
 
 private:
-  ScopeChecker Scope;
-  ArithmeticArgChecker ArithmeticArg;
-  TrivialCtorDtorChecker TrivialCtorDtor;
-  NaNExprChecker NaNExpr;
-  NoAddRefReleaseOnReturnChecker NoAddRefReleaseOnReturn;
-  RefCountedInsideLambdaChecker RefCountedInsideLambda;
-  ExplicitOperatorBoolChecker ExplicitOperatorBool;
-  NoDuplicateRefCntMemberChecker NoDuplicateRefCntMember;
-  NeedsNoVTableTypeChecker NeedsNoVTableType;
-  NonMemMovableTemplateArgChecker NonMemMovableTemplateArg;
-  NonMemMovableMemberChecker NonMemMovableMember;
-  ExplicitImplicitChecker ExplicitImplicit;
-  NoAutoTypeChecker NoAutoType;
-  NoExplicitMoveConstructorChecker NoExplicitMoveConstructor;
-  RefCountedCopyConstructorChecker RefCountedCopyConstructor;
-  AssertAssignmentChecker AssertAssignment;
-  KungFuDeathGripChecker KungFuDeathGrip;
-  SprintfLiteralChecker SprintfLiteral;
-  OverrideBaseCallChecker OverrideBaseCall;
-  OverrideBaseCallUsageChecker OverrideBaseCallUsage;
-  NonParamInsideFunctionDeclChecker NonParamInsideFunctionDecl;
+#define CHECK(cls, name) cls cls ## _;
+#include "Checks.inc"
+#undef CHECK
   MatchFinder AstMatcher;
 };
 
 #endif
--- a/build/clang-plugin/ExplicitImplicitChecker.cpp
+++ b/build/clang-plugin/ExplicitImplicitChecker.cpp
@@ -1,36 +1,31 @@
 /* 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 "ExplicitImplicitChecker.h"
 #include "CustomMatchers.h"
 
-void ExplicitImplicitChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(),
-                                           ofClass(allOf(isConcreteClass(),
-                                                         decl().bind("class"))),
-                                           unless(isMarkedImplicit()))
+void ExplicitImplicitChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(),
+                                            ofClass(allOf(isConcreteClass(),
+                                                          decl().bind("class"))),
+                                            unless(isMarkedImplicit()))
                             .bind("ctor"),
                         this);
 }
 
-void ExplicitImplicitChecker::run(
+void ExplicitImplicitChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "bad implicit conversion constructor for %0");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note,
-      "consider adding the explicit keyword to the constructor");
-
   // We've already checked everything in the matcher, so we just have to report
   // the error.
 
   const CXXConstructorDecl *Ctor =
       Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
   const CXXRecordDecl *Declaration =
       Result.Nodes.getNodeAs<CXXRecordDecl>("class");
 
-  Diag.Report(Ctor->getLocation(), ErrorID) << Declaration->getDeclName();
-  Diag.Report(Ctor->getLocation(), NoteID);
+  diag(Ctor->getLocation(), "bad implicit conversion constructor for %0",
+       DiagnosticIDs::Error) << Declaration->getDeclName();
+  diag(Ctor->getLocation(), "consider adding the explicit keyword to the constructor",
+       DiagnosticIDs::Note);
 }
--- a/build/clang-plugin/ExplicitImplicitChecker.h
+++ b/build/clang-plugin/ExplicitImplicitChecker.h
@@ -2,15 +2,18 @@
  * 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 ExplicitImplicitChecker_h__
 #define ExplicitImplicitChecker_h__
 
 #include "plugin.h"
 
-class ExplicitImplicitChecker : public MatchFinder::MatchCallback {
+class ExplicitImplicitChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  ExplicitImplicitChecker(StringRef CheckName,
+                          ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/ExplicitOperatorBoolChecker.cpp
+++ b/build/clang-plugin/ExplicitOperatorBoolChecker.cpp
@@ -1,36 +1,33 @@
 /* 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 "ExplicitOperatorBoolChecker.h"
 #include "CustomMatchers.h"
 
-void ExplicitOperatorBoolChecker::registerMatcher(MatchFinder& AstMatcher) {
+void ExplicitOperatorBoolChecker::registerMatchers(MatchFinder* AstMatcher) {
   // Older clang versions such as the ones used on the infra recognize these
   // conversions as 'operator _Bool', but newer clang versions recognize these
   // as 'operator bool'.
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       cxxMethodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool")))
           .bind("node"),
       this);
 }
 
-void ExplicitOperatorBoolChecker::run(
+void ExplicitOperatorBoolChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "bad implicit conversion operator for %0");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "consider adding the explicit keyword to %0");
   const CXXConversionDecl *Method =
       Result.Nodes.getNodeAs<CXXConversionDecl>("node");
   const CXXRecordDecl *Clazz = Method->getParent();
 
   if (!Method->isExplicitSpecified() &&
       !MozChecker::hasCustomAnnotation(Method, "moz_implicit") &&
       !ASTIsInSystemHeader(Method->getASTContext(), *Method) &&
       isInterestingDeclForImplicitConversion(Method)) {
-    Diag.Report(Method->getLocStart(), ErrorID) << Clazz;
-    Diag.Report(Method->getLocStart(), NoteID) << "'operator bool'";
+    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/ExplicitOperatorBoolChecker.h
+++ b/build/clang-plugin/ExplicitOperatorBoolChecker.h
@@ -2,15 +2,18 @@
  * 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 ExplicitOperatorBoolChecker_h__
 #define ExplicitOperatorBoolChecker_h__
 
 #include "plugin.h"
 
-class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback {
+class ExplicitOperatorBoolChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  ExplicitOperatorBoolChecker(StringRef CheckName,
+                              ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/KungFuDeathGripChecker.cpp
+++ b/build/clang-plugin/KungFuDeathGripChecker.cpp
@@ -1,30 +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 "KungFuDeathGripChecker.h"
 #include "CustomMatchers.h"
 
-void KungFuDeathGripChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(varDecl(hasType(isRefPtr())).bind("decl"),
-                        this);
+void KungFuDeathGripChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(varDecl(hasType(isRefPtr())).bind("decl"),
+                         this);
 }
 
-void KungFuDeathGripChecker::run(
+void KungFuDeathGripChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "Unused \"kungFuDeathGrip\" %0 objects constructed from %1 are prohibited");
-
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note,
-      "Please switch all accesses to this %0 to go through '%1', or explicitly pass '%1' to `mozilla::Unused`");
+  const char* Error =
+    "Unused \"kungFuDeathGrip\" %0 objects constructed from %1 are prohibited";
+  const char* Note =
+    "Please switch all accesses to this %0 to go through '%1', or explicitly pass '%1' to `mozilla::Unused`";
 
   const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("decl");
   if (D->isReferenced() || !D->hasLocalStorage() || !D->hasInit()) {
     return;
   }
 
   // Not interested in parameters.
   if (isa<ImplicitParamDecl>(D) || isa<ParmVarDecl>(D)) {
@@ -92,11 +88,11 @@ void KungFuDeathGripChecker::run(
     ErrThing  = "members";
     NoteThing = "member";
   } else {
     ErrThing = "temporary values";
     NoteThing = "value";
   }
 
   // We cannot provide the note if we don't have an initializer
-  Diag.Report(D->getLocStart(), ErrorID) << D->getType() << ErrThing;
-  Diag.Report(E->getLocStart(), NoteID) << NoteThing << getNameChecked(D);
+  diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D->getType() << ErrThing;
+  diag(E->getLocStart(), Note, DiagnosticIDs::Note) << NoteThing << getNameChecked(D);
 }
--- a/build/clang-plugin/KungFuDeathGripChecker.h
+++ b/build/clang-plugin/KungFuDeathGripChecker.h
@@ -2,15 +2,18 @@
  * 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 KungFuDeathGripChecker_h__
 #define KungFuDeathGripChecker_h__
 
 #include "plugin.h"
 
-class KungFuDeathGripChecker : public MatchFinder::MatchCallback {
+class KungFuDeathGripChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  KungFuDeathGripChecker(StringRef CheckName,
+                         ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/MozillaTidyModule.cpp
@@ -0,0 +1,37 @@
+/* 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/. */
+
+#ifdef CLANG_TIDY
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "ChecksIncludes.inc"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+
+class MozillaModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+#define CHECK(cls, name) CheckFactories.registerCheck<cls>("mozilla-" name);
+#include "Checks.inc"
+#undef CHECK
+  }
+};
+
+// Register the MozillaTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<MozillaModule> X("mozilla-module",
+                                                     "Adds Mozilla lint checks.");
+
+} // namespace tidy
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the MozillaModule.
+volatile int MozillaModuleAnchorSource = 0;
+
+#endif
--- a/build/clang-plugin/NaNExprChecker.cpp
+++ b/build/clang-plugin/NaNExprChecker.cpp
@@ -1,42 +1,36 @@
 /* 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 "NaNExprChecker.h"
 #include "CustomMatchers.h"
 
-void NaNExprChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void NaNExprChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       binaryOperator(
           allOf(binaryEqualityOperator(),
                 hasLHS(hasIgnoringParenImpCasts(
                     declRefExpr(hasType(qualType((isFloat())))).bind("lhs"))),
                 hasRHS(hasIgnoringParenImpCasts(
                     declRefExpr(hasType(qualType((isFloat())))).bind("rhs"))),
                 unless(anyOf(isInSystemHeader(), isInWhitelistForNaNExpr()))))
           .bind("node"),
       this);
 }
 
-void NaNExprChecker::run(
+void NaNExprChecker::check(
     const MatchFinder::MatchResult &Result) {
   if (!Result.Context->getLangOpts().CPlusPlus) {
     // mozilla::IsNaN is not usable in C, so there is no point in issuing these
     // warnings.
     return;
   }
 
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "comparing a floating point value to itself for "
-                            "NaN checking can lead to incorrect results");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "consider using mozilla::IsNaN instead");
   const BinaryOperator *Expression = Result.Nodes.getNodeAs<BinaryOperator>(
     "node");
   const DeclRefExpr *LHS = Result.Nodes.getNodeAs<DeclRefExpr>("lhs");
   const DeclRefExpr *RHS = Result.Nodes.getNodeAs<DeclRefExpr>("rhs");
   const ImplicitCastExpr *LHSExpr = dyn_cast<ImplicitCastExpr>(
     Expression->getLHS());
   const ImplicitCastExpr *RHSExpr = dyn_cast<ImplicitCastExpr>(
     Expression->getRHS());
@@ -48,12 +42,15 @@ void NaNExprChecker::run(
   //    |-DeclRefExpr
   // The check below ensures that we are dealing with the correct AST subtree
   // shape, and
   // also that both of the found DeclRefExpr's point to the same declaration.
   if (LHS->getFoundDecl() == RHS->getFoundDecl() && LHSExpr && RHSExpr &&
       std::distance(LHSExpr->child_begin(), LHSExpr->child_end()) == 1 &&
       std::distance(RHSExpr->child_begin(), RHSExpr->child_end()) == 1 &&
       *LHSExpr->child_begin() == LHS && *RHSExpr->child_begin() == RHS) {
-    Diag.Report(Expression->getLocStart(), ErrorID);
-    Diag.Report(Expression->getLocStart(), NoteID);
+    diag(Expression->getLocStart(), "comparing a floating point value to itself for "
+                                    "NaN checking can lead to incorrect results",
+         DiagnosticIDs::Error);
+    diag(Expression->getLocStart(), "consider using mozilla::IsNaN instead",
+         DiagnosticIDs::Note);
   }
 }
--- a/build/clang-plugin/NaNExprChecker.h
+++ b/build/clang-plugin/NaNExprChecker.h
@@ -2,15 +2,18 @@
  * 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 NaNExprChecker_h__
 #define NaNExprChecker_h__
 
 #include "plugin.h"
 
-class NaNExprChecker : public MatchFinder::MatchCallback {
+class NaNExprChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NaNExprChecker(StringRef CheckName,
+                 ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NeedsNoVTableTypeChecker.cpp
+++ b/build/clang-plugin/NeedsNoVTableTypeChecker.cpp
@@ -1,44 +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/. */
 
 #include "NeedsNoVTableTypeChecker.h"
 #include "CustomMatchers.h"
 
-void NeedsNoVTableTypeChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void NeedsNoVTableTypeChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       classTemplateSpecializationDecl(
           allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
                 hasNeedsNoVTableTypeAttr()))
           .bind("node"),
       this);
 }
 
-void NeedsNoVTableTypeChecker::run(
+void NeedsNoVTableTypeChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "%0 cannot be instantiated because %1 has a VTable");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "bad instantiation of %0 requested here");
-
   const ClassTemplateSpecializationDecl *Specialization =
       Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node");
 
   // Get the offending template argument
   QualType Offender;
   const TemplateArgumentList &Args =
       Specialization->getTemplateInstantiationArgs();
   for (unsigned i = 0; i < Args.size(); ++i) {
     Offender = Args[i].getAsType();
     if (typeHasVTable(Offender)) {
       break;
     }
   }
 
-  Diag.Report(Specialization->getLocStart(), ErrorID) << Specialization
-                                                      << Offender;
-  Diag.Report(Specialization->getPointOfInstantiation(), NoteID)
-      << Specialization;
+  diag(Specialization->getLocStart(),
+       "%0 cannot be instantiated because %1 has a VTable",
+       DiagnosticIDs::Error) << Specialization
+                             << Offender;
+  diag(Specialization->getPointOfInstantiation(),
+       "bad instantiation of %0 requested here",
+       DiagnosticIDs::Note)  << Specialization;
 }
--- a/build/clang-plugin/NeedsNoVTableTypeChecker.h
+++ b/build/clang-plugin/NeedsNoVTableTypeChecker.h
@@ -2,15 +2,18 @@
  * 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 NeedsNoVTableTypeChecker_h__
 #define NeedsNoVTableTypeChecker_h__
 
 #include "plugin.h"
 
-class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback {
+class NeedsNoVTableTypeChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NeedsNoVTableTypeChecker(StringRef CheckName,
+                           ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
+++ b/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
@@ -1,41 +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/. */
 
 #include "NoAddRefReleaseOnReturnChecker.h"
 #include "CustomMatchers.h"
 
-void NoAddRefReleaseOnReturnChecker::registerMatcher(MatchFinder& AstMatcher) {
+void NoAddRefReleaseOnReturnChecker::registerMatchers(MatchFinder* AstMatcher) {
   // First, look for direct parents of the MemberExpr.
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       callExpr(
           callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
           hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
                         .bind("member")))
           .bind("node"),
       this);
   // Then, look for MemberExpr that need to be casted to the right type using
   // an intermediary CastExpr before we get to the CallExpr.
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       callExpr(
           callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
           hasParent(castExpr(
               hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
                             .bind("member")))))
           .bind("node"),
       this);
 }
 
-void NoAddRefReleaseOnReturnChecker::run(
+void NoAddRefReleaseOnReturnChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "%1 cannot be called on the return value of %0");
   const Stmt *Node = Result.Nodes.getNodeAs<Stmt>("node");
   const FunctionDecl *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
   const MemberExpr *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
   const CXXMethodDecl *Method =
       dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
 
-  Diag.Report(Node->getLocStart(), ErrorID) << Func << Method;
+  diag(Node->getLocStart(),
+       "%1 cannot be called on the return value of %0",
+       DiagnosticIDs::Error) << Func << Method;
 }
--- a/build/clang-plugin/NoAddRefReleaseOnReturnChecker.h
+++ b/build/clang-plugin/NoAddRefReleaseOnReturnChecker.h
@@ -2,15 +2,18 @@
  * 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 NoAddRefReleaseOnReturnChecker_h__
 #define NoAddRefReleaseOnReturnChecker_h__
 
 #include "plugin.h"
 
-class NoAddRefReleaseOnReturnChecker : public MatchFinder::MatchCallback {
+class NoAddRefReleaseOnReturnChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NoAddRefReleaseOnReturnChecker(StringRef CheckName,
+                                 ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NoAutoTypeChecker.cpp
+++ b/build/clang-plugin/NoAutoTypeChecker.cpp
@@ -1,25 +1,23 @@
 /* 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 "NoAutoTypeChecker.h"
 #include "CustomMatchers.h"
 
-void NoAutoTypeChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"),
-                        this);
+void NoAutoTypeChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"),
+                         this);
 }
 
-void NoAutoTypeChecker::run(
+void NoAutoTypeChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "Cannot use auto to declare a variable of type %0");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "Please write out this type explicitly");
-
   const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node");
 
-  Diag.Report(D->getLocation(), ErrorID) << D->getType();
-  Diag.Report(D->getLocation(), NoteID);
+  diag(D->getLocation(),
+       "Cannot use auto to declare a variable of type %0",
+       DiagnosticIDs::Error) << D->getType();
+  diag(D->getLocation(),
+       "Please write out this type explicitly",
+       DiagnosticIDs::Note);
 }
--- a/build/clang-plugin/NoAutoTypeChecker.h
+++ b/build/clang-plugin/NoAutoTypeChecker.h
@@ -2,15 +2,18 @@
  * 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 NoAutoTypeChecker_h__
 #define NoAutoTypeChecker_h__
 
 #include "plugin.h"
 
-class NoAutoTypeChecker : public MatchFinder::MatchCallback {
+class NoAutoTypeChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NoAutoTypeChecker(StringRef CheckName,
+                    ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NoDuplicateRefCntMemberChecker.cpp
+++ b/build/clang-plugin/NoDuplicateRefCntMemberChecker.cpp
@@ -1,22 +1,21 @@
 /* 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 "NoDuplicateRefCntMemberChecker.h"
 #include "CustomMatchers.h"
 
-void NoDuplicateRefCntMemberChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(cxxRecordDecl().bind("decl"), this);
+void NoDuplicateRefCntMemberChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(cxxRecordDecl().bind("decl"), this);
 }
 
-void NoDuplicateRefCntMemberChecker::run(
+void NoDuplicateRefCntMemberChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   const CXXRecordDecl *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
   const FieldDecl *RefCntMember = getClassRefCntMember(D);
   const FieldDecl *FoundRefCntBase = nullptr;
 
   if (!D->hasDefinition())
     return;
   D = D->getDefinition();
 
@@ -30,44 +29,40 @@ void NoDuplicateRefCntMemberChecker::run
   // warn for those which do
   for (auto &Base : D->bases()) {
     // Determine if this base class has an mRefCnt member
     const FieldDecl *BaseRefCntMember = getBaseRefCntMember(Base.getType());
 
     if (BaseRefCntMember) {
       if (RefCntMember) {
         // We have an mRefCnt, and superclass has an mRefCnt
-        unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Error,
-            "Refcounted record %0 has multiple mRefCnt members");
-        unsigned Note1 = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Note, "Superclass %0 also has an mRefCnt member");
-        unsigned Note2 = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Note,
-            "Consider using the _INHERITED macros for AddRef and Release here");
+        const char* Error =
+            "Refcounted record %0 has multiple mRefCnt members";
+        const char* Note1 =
+            "Superclass %0 also has an mRefCnt member";
+        const char* Note2 =
+            "Consider using the _INHERITED macros for AddRef and Release here";
 
-        Diag.Report(D->getLocStart(), Error) << D;
-        Diag.Report(BaseRefCntMember->getLocStart(), Note1)
+        diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D;
+        diag(BaseRefCntMember->getLocStart(), Note1, DiagnosticIDs::Note)
           << BaseRefCntMember->getParent();
-        Diag.Report(RefCntMember->getLocStart(), Note2);
+        diag(RefCntMember->getLocStart(), Note2, DiagnosticIDs::Note);
       }
 
       if (FoundRefCntBase) {
-        unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Error,
-            "Refcounted record %0 has multiple superclasses with mRefCnt members");
-        unsigned Note = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Note,
-            "Superclass %0 has an mRefCnt member");
+        const char* Error =
+            "Refcounted record %0 has multiple superclasses with mRefCnt members";
+        const char* Note =
+            "Superclass %0 has an mRefCnt member";
 
         // superclass has mRefCnt, and another superclass also has an mRefCnt
-        Diag.Report(D->getLocStart(), Error) << D;
-        Diag.Report(BaseRefCntMember->getLocStart(), Note)
+        diag(D->getLocStart(), Error, DiagnosticIDs::Error) << D;
+        diag(BaseRefCntMember->getLocStart(), Note, DiagnosticIDs::Note)
           << BaseRefCntMember->getParent();
-        Diag.Report(FoundRefCntBase->getLocStart(), Note)
+        diag(FoundRefCntBase->getLocStart(), Note, DiagnosticIDs::Note)
           << FoundRefCntBase->getParent();
       }
 
       // Record that we've found a base with a mRefCnt member
       FoundRefCntBase = BaseRefCntMember;
     }
   }
 }
--- a/build/clang-plugin/NoDuplicateRefCntMemberChecker.h
+++ b/build/clang-plugin/NoDuplicateRefCntMemberChecker.h
@@ -2,15 +2,18 @@
  * 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 NoDuplicateRefCntMemberChecker_h__
 #define NoDuplicateRefCntMemberChecker_h__
 
 #include "plugin.h"
 
-class NoDuplicateRefCntMemberChecker : public MatchFinder::MatchCallback {
+class NoDuplicateRefCntMemberChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NoDuplicateRefCntMemberChecker(StringRef CheckName,
+                                 ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NoExplicitMoveConstructorChecker.cpp
+++ b/build/clang-plugin/NoExplicitMoveConstructorChecker.cpp
@@ -1,26 +1,23 @@
 /* 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 "NoExplicitMoveConstructorChecker.h"
 #include "CustomMatchers.h"
 
-void NoExplicitMoveConstructorChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void NoExplicitMoveConstructorChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"),
       this);
 }
 
-void NoExplicitMoveConstructorChecker::run(
+void NoExplicitMoveConstructorChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "Move constructors may not be marked explicit");
-
   // Everything we needed to know was checked in the matcher - we just report
   // the error here
   const CXXConstructorDecl *D =
       Result.Nodes.getNodeAs<CXXConstructorDecl>("node");
 
-  Diag.Report(D->getLocation(), ErrorID);
+  diag(D->getLocation(), "Move constructors may not be marked explicit",
+       DiagnosticIDs::Error);
 }
--- a/build/clang-plugin/NoExplicitMoveConstructorChecker.h
+++ b/build/clang-plugin/NoExplicitMoveConstructorChecker.h
@@ -2,15 +2,18 @@
  * 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 NoExplicitMoveConstructorChecker_h__
 #define NoExplicitMoveConstructorChecker_h__
 
 #include "plugin.h"
 
-class NoExplicitMoveConstructorChecker : public MatchFinder::MatchCallback {
+class NoExplicitMoveConstructorChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NoExplicitMoveConstructorChecker(StringRef CheckName,
+                                   ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NonMemMovableMemberChecker.cpp
+++ b/build/clang-plugin/NonMemMovableMemberChecker.cpp
@@ -2,38 +2,37 @@
  * 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 "NonMemMovableMemberChecker.h"
 #include "CustomMatchers.h"
 
 MemMoveAnnotation NonMemMovable = MemMoveAnnotation();
 
-void NonMemMovableMemberChecker::registerMatcher(MatchFinder& AstMatcher) {
+void NonMemMovableMemberChecker::registerMatchers(MatchFinder* AstMatcher) {
   // Handle non-mem-movable members
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       cxxRecordDecl(needsMemMovableMembers())
           .bind("decl"),
       this);
 }
 
-void NonMemMovableMemberChecker::run(
+void NonMemMovableMemberChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "class %0 cannot have non-memmovable member %1 of type %2");
+  const char* Error =
+      "class %0 cannot have non-memmovable member %1 of type %2";
 
   // Get the specialization
   const CXXRecordDecl* Declaration =
       Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
 
   // Report an error for every member which is non-memmovable
   for (const FieldDecl *Field : Declaration->fields()) {
     QualType Type = Field->getType();
     if (NonMemMovable.hasEffectiveAnnotation(Type)) {
-      Diag.Report(Field->getLocation(), ErrorID) << Declaration
-                                                 << Field
-                                                 << Type;
-      NonMemMovable.dumpAnnotationReason(Diag, Type, Declaration->getLocation());
+      diag(Field->getLocation(), Error, DiagnosticIDs::Error)
+        << Declaration
+        << Field
+        << Type;
+      NonMemMovable.dumpAnnotationReason(*this, Type, Declaration->getLocation());
     }
   }
 }
--- a/build/clang-plugin/NonMemMovableMemberChecker.h
+++ b/build/clang-plugin/NonMemMovableMemberChecker.h
@@ -2,15 +2,18 @@
  * 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 NonMemMovableMemberChecker_h__
 #define NonMemMovableMemberChecker_h__
 
 #include "plugin.h"
 
-class NonMemMovableMemberChecker : public MatchFinder::MatchCallback {
+class NonMemMovableMemberChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NonMemMovableMemberChecker(StringRef CheckName,
+                             ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NonMemMovableTemplateArgChecker.cpp
+++ b/build/clang-plugin/NonMemMovableTemplateArgChecker.cpp
@@ -1,53 +1,52 @@
 /* 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 "NonMemMovableTemplateArgChecker.h"
 #include "CustomMatchers.h"
 
-void NonMemMovableTemplateArgChecker::registerMatcher(MatchFinder& AstMatcher) {
+void NonMemMovableTemplateArgChecker::registerMatchers(MatchFinder* AstMatcher) {
   // Handle non-mem-movable template specializations
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       classTemplateSpecializationDecl(
           allOf(needsMemMovableTemplateArg(),
                 hasAnyTemplateArgument(refersToType(isNonMemMovable()))))
           .bind("specialization"),
       this);
 }
 
-void NonMemMovableTemplateArgChecker::run(
+void NonMemMovableTemplateArgChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "Cannot instantiate %0 with non-memmovable template argument %1");
-  unsigned Note1ID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "instantiation of %0 requested here");
+  const char* Error =
+      "Cannot instantiate %0 with non-memmovable template argument %1";
+  const char* Note =
+      "instantiation of %0 requested here";
 
   // Get the specialization
   const ClassTemplateSpecializationDecl *Specialization =
       Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
   SourceLocation RequestLoc = Specialization->getPointOfInstantiation();
 
   // Report an error for every template argument which is non-memmovable
   const TemplateArgumentList &Args =
       Specialization->getTemplateInstantiationArgs();
   for (unsigned i = 0; i < Args.size(); ++i) {
     QualType ArgType = Args[i].getAsType();
     if (NonMemMovable.hasEffectiveAnnotation(ArgType)) {
-      Diag.Report(Specialization->getLocation(), ErrorID) << Specialization
-                                                          << ArgType;
+      diag(Specialization->getLocation(), Error,
+           DiagnosticIDs::Error) << Specialization
+                                 << ArgType;
       // XXX It would be really nice if we could get the instantiation stack
       // information
       // from Sema such that we could print a full template instantiation stack,
       // however,
       // it seems as though that information is thrown out by the time we get
       // here so we
       // can only report one level of template specialization (which in many
       // cases won't
       // be useful)
-      Diag.Report(RequestLoc, Note1ID) << Specialization;
-      NonMemMovable.dumpAnnotationReason(Diag, ArgType, RequestLoc);
+      diag(RequestLoc, Note, DiagnosticIDs::Note) << Specialization;
+      NonMemMovable.dumpAnnotationReason(*this, ArgType, RequestLoc);
     }
   }
 }
--- a/build/clang-plugin/NonMemMovableTemplateArgChecker.h
+++ b/build/clang-plugin/NonMemMovableTemplateArgChecker.h
@@ -2,15 +2,18 @@
  * 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 NonMemMovableTemplateArgChecker_h__
 #define NonMemMovableTemplateArgChecker_h__
 
 #include "plugin.h"
 
-class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback {
+class NonMemMovableTemplateArgChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NonMemMovableTemplateArgChecker(StringRef CheckName,
+                                  ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
+++ b/build/clang-plugin/NonParamInsideFunctionDeclChecker.cpp
@@ -1,29 +1,29 @@
 /* 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 "NonParamInsideFunctionDeclChecker.h"
 #include "CustomMatchers.h"
 
-void NonParamInsideFunctionDeclChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void NonParamInsideFunctionDeclChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       functionDecl(anyOf(allOf(isDefinition(),
                                hasAncestor(classTemplateSpecializationDecl()
                                                .bind("spec"))),
                          isDefinition()))
           .bind("func"),
       this);
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       lambdaExpr().bind("lambda"),
       this);
 }
 
-void NonParamInsideFunctionDeclChecker::run(
+void NonParamInsideFunctionDeclChecker::check(
     const MatchFinder::MatchResult &Result) {
   static DenseSet<const FunctionDecl*> CheckedFunctionDecls;
 
   const FunctionDecl *func = Result.Nodes.getNodeAs<FunctionDecl>("func");
   if (!func) {
     const LambdaExpr *lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
     if (lambda) {
       func = lambda->getCallOperator();
@@ -42,29 +42,25 @@ void NonParamInsideFunctionDeclChecker::
   if (CheckedFunctionDecls.count(func)) {
     return;
   }
   CheckedFunctionDecls.insert(func);
 
   const ClassTemplateSpecializationDecl *Spec =
       Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("spec");
 
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "Type %0 must not be used as parameter");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "Please consider passing a const reference instead");
-  unsigned SpecNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "The bad argument was passed to %0 here");
-
   for (ParmVarDecl *p : func->parameters()) {
     QualType T = p->getType().withoutLocalFastQualifiers();
     if (NonParam.hasEffectiveAnnotation(T)) {
-      Diag.Report(p->getLocation(), ErrorID) << T;
-      Diag.Report(p->getLocation(), NoteID);
+      diag(p->getLocation(), "Type %0 must not be used as parameter",
+           DiagnosticIDs::Error) << T;
+      diag(p->getLocation(), "Please consider passing a const reference instead",
+           DiagnosticIDs::Note);
 
       if (Spec) {
-        Diag.Report(Spec->getPointOfInstantiation(), SpecNoteID)
+        diag(Spec->getPointOfInstantiation(),
+             "The bad argument was passed to %0 here",
+             DiagnosticIDs::Note)
           << Spec->getSpecializedTemplate();
       }
     }
   }
 }
--- a/build/clang-plugin/NonParamInsideFunctionDeclChecker.h
+++ b/build/clang-plugin/NonParamInsideFunctionDeclChecker.h
@@ -2,15 +2,18 @@
  * 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 NonParamInsideFunctionDeclChecker_h__
 #define NonParamInsideFunctionDeclChecker_h__
 
 #include "plugin.h"
 
-class NonParamInsideFunctionDeclChecker : public MatchFinder::MatchCallback {
+class NonParamInsideFunctionDeclChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  NonParamInsideFunctionDeclChecker(StringRef CheckName,
+                                    ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/OverrideBaseCallChecker.cpp
+++ b/build/clang-plugin/OverrideBaseCallChecker.cpp
@@ -1,17 +1,17 @@
 /* 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 "OverrideBaseCallChecker.h"
 #include "CustomMatchers.h"
 
-void OverrideBaseCallChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"),
+void OverrideBaseCallChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"),
       this);
 }
 
 bool OverrideBaseCallChecker::isRequiredBaseMethod(
     const CXXMethodDecl *Method) {
   return MozChecker::hasCustomAnnotation(Method, "moz_required_base_method");
 }
 
@@ -58,23 +58,21 @@ void OverrideBaseCallChecker::findBaseMe
   MethodsList.remove(Method);
   // Loop also through all it's base methods;
   for (auto BaseMethod = Method->begin_overridden_methods();
       BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
     findBaseMethodCall(*BaseMethod, MethodsList);
   }
 }
 
-void OverrideBaseCallChecker::run(
+void OverrideBaseCallChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned OverrideBaseCallCheckID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
+  const char* Error =
       "Method %0 must be called in all overrides, but is not called in "
-      "this override defined for class %1");
+      "this override defined for class %1";
   const CXXRecordDecl *Decl = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
 
   // Loop through the methods and look for the ones that are overridden.
   for (auto Method : Decl->methods()) {
     // If this method doesn't override other methods or it doesn't have a body,
     // continue to the next declaration.
     if (!Method->size_overridden_methods() || !Method->hasBody()) {
       continue;
@@ -97,14 +95,14 @@ void OverrideBaseCallChecker::run(
     }
 
     // Loop through the body of our method and search for calls to
     // base methods
     evaluateExpression(Method->getBody(), MethodsList);
 
     // If list is not empty pop up errors
     for (auto BaseMethod : MethodsList) {
-      Diag.Report(Method->getLocation(), OverrideBaseCallCheckID)
+      diag(Method->getLocation(), Error, DiagnosticIDs::Error)
           << BaseMethod->getQualifiedNameAsString()
           << Decl->getName();
     }
   }
 }
--- a/build/clang-plugin/OverrideBaseCallChecker.h
+++ b/build/clang-plugin/OverrideBaseCallChecker.h
@@ -2,20 +2,23 @@
  * 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 OverrideBaseCallChecker_h__
 #define OverrideBaseCallChecker_h__
 
 #include "plugin.h"
 
-class OverrideBaseCallChecker : public MatchFinder::MatchCallback {
+class OverrideBaseCallChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  OverrideBaseCallChecker(StringRef CheckName,
+                          ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 private:
   void evaluateExpression(const Stmt *StmtExpr,
       std::list<const CXXMethodDecl*> &MethodList);
   void getRequiredBaseMethod(const CXXMethodDecl* Method,
       std::list<const CXXMethodDecl*>& MethodsList);
   void findBaseMethodCall(const CXXMethodDecl* Method,
       std::list<const CXXMethodDecl*>& MethodsList);
   bool isRequiredBaseMethod(const CXXMethodDecl *Method);
--- a/build/clang-plugin/OverrideBaseCallUsageChecker.cpp
+++ b/build/clang-plugin/OverrideBaseCallUsageChecker.cpp
@@ -1,23 +1,21 @@
 /* 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 "OverrideBaseCallUsageChecker.h"
 #include "CustomMatchers.h"
 
-void OverrideBaseCallUsageChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void OverrideBaseCallUsageChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"),
       this);
 }
 
-void OverrideBaseCallUsageChecker::run(
+void OverrideBaseCallUsageChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods");
+  const char* Error =
+      "MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods";
   const CXXMethodDecl *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
 
-  Diag.Report(Method->getLocation(), ErrorID);
+  diag(Method->getLocation(), Error, DiagnosticIDs::Error);
 }
--- a/build/clang-plugin/OverrideBaseCallUsageChecker.h
+++ b/build/clang-plugin/OverrideBaseCallUsageChecker.h
@@ -6,15 +6,18 @@
 #define OverrideBaseCallUsageChecker_h__
 
 #include "plugin.h"
 
 /*
  *  This is a companion checker for OverrideBaseCallChecker that rejects
  *  the usage of MOZ_REQUIRED_BASE_METHOD on non-virtual base methods.
  */
-class OverrideBaseCallUsageChecker : public MatchFinder::MatchCallback {
+class OverrideBaseCallUsageChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  OverrideBaseCallUsageChecker(StringRef CheckName = "override-base-call-usage",
+                               ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/RefCountedCopyConstructorChecker.cpp
+++ b/build/clang-plugin/RefCountedCopyConstructorChecker.cpp
@@ -1,37 +1,34 @@
 /* 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 "RefCountedCopyConstructorChecker.h"
 #include "CustomMatchers.h"
 
-void RefCountedCopyConstructorChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void RefCountedCopyConstructorChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       cxxConstructExpr(
           hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
                                             ofClass(hasRefCntMember()))))
           .bind("node"),
       this);
 }
 
-void RefCountedCopyConstructorChecker::run(
+void RefCountedCopyConstructorChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "Invalid use of compiler-provided copy constructor "
-                            "on refcounted type");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note,
+  const char* Error =
+      "Invalid use of compiler-provided copy constructor on refcounted type";
+  const char* Note =
       "The default copy constructor also copies the "
       "default mRefCnt property, leading to reference "
       "count imbalance issues. Please provide your own "
       "copy constructor which only copies the fields which "
-      "need to be copied");
+      "need to be copied";
 
   // Everything we needed to know was checked in the matcher - we just report
   // the error here
   const CXXConstructExpr *E = Result.Nodes.getNodeAs<CXXConstructExpr>("node");
 
-  Diag.Report(E->getLocation(), ErrorID);
-  Diag.Report(E->getLocation(), NoteID);
+  diag(E->getLocation(), Error, DiagnosticIDs::Error);
+  diag(E->getLocation(), Note, DiagnosticIDs::Note);
 }
--- a/build/clang-plugin/RefCountedCopyConstructorChecker.h
+++ b/build/clang-plugin/RefCountedCopyConstructorChecker.h
@@ -2,15 +2,18 @@
  * 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 RefCountedCopyConstructorChecker_h__
 #define RefCountedCopyConstructorChecker_h__
 
 #include "plugin.h"
 
-class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback {
+class RefCountedCopyConstructorChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  RefCountedCopyConstructorChecker(StringRef CheckName,
+                                   ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
@@ -2,47 +2,45 @@
  * 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 "RefCountedInsideLambdaChecker.h"
 #include "CustomMatchers.h"
 
 RefCountedMap RefCountedClasses;
 
-void RefCountedInsideLambdaChecker::registerMatcher(MatchFinder& AstMatcher) {
+void RefCountedInsideLambdaChecker::registerMatchers(MatchFinder* AstMatcher) {
   // We want to reject any code which captures a pointer to an object of a
   // refcounted type, and then lets that value escape. As a primitive analysis,
   // we reject any occurances of the lambda as a template parameter to a class
   // (which could allow it to escape), as well as any presence of such a lambda
   // in a return value (either from lambdas, or in c++14, auto functions).
   //
   // We check these lambdas' capture lists for raw pointers to refcounted types.
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       functionDecl(returns(recordType(hasDeclaration(cxxRecordDecl(
         isLambdaDecl()).bind("decl"))))),
       this);
-  AstMatcher.addMatcher(lambdaExpr().bind("lambdaExpr"),
+  AstMatcher->addMatcher(lambdaExpr().bind("lambdaExpr"),
       this);
-  AstMatcher.addMatcher(
+  AstMatcher->addMatcher(
       classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
         recordType(hasDeclaration(cxxRecordDecl(
           isLambdaDecl()).bind("decl")))))),
       this);
 }
 
-void RefCountedInsideLambdaChecker::run(
+void RefCountedInsideLambdaChecker::check(
     const MatchFinder::MatchResult &Result) {
   static DenseSet<const CXXRecordDecl*> CheckedDecls;
 
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "Refcounted variable %0 of type %1 cannot be captured by a lambda");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "Please consider using a smart pointer");
+  const char* Error =
+      "Refcounted variable %0 of type %1 cannot be captured by a lambda";
+  const char* Note =
+      "Please consider using a smart pointer";
 
   const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
 
   if (const LambdaExpr *OuterLambda =
     Result.Nodes.getNodeAs<LambdaExpr>("lambdaExpr")) {
     const CXXMethodDecl *OpCall = OuterLambda->getCallOperator();
     QualType ReturnTy = OpCall->getReturnType();
     if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) {
@@ -60,16 +58,18 @@ void RefCountedInsideLambdaChecker::run(
   }
   CheckedDecls.insert(Lambda);
 
   for (const LambdaCapture Capture : Lambda->captures()) {
     if (Capture.capturesVariable() && Capture.getCaptureKind() != LCK_ByRef) {
       QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
 
       if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
-        Diag.Report(Capture.getLocation(), ErrorID) << Capture.getCapturedVar()
-                                                    << Pointee;
-        Diag.Report(Capture.getLocation(), NoteID);
+        diag(Capture.getLocation(), Error,
+             DiagnosticIDs::Error) << Capture.getCapturedVar()
+                                   << Pointee;
+        diag(Capture.getLocation(), Note,
+             DiagnosticIDs::Note);
         return;
       }
     }
   }
 }
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.h
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.h
@@ -2,15 +2,18 @@
  * 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 RefCountedInsideLambdaChecker_h__
 #define RefCountedInsideLambdaChecker_h__
 
 #include "plugin.h"
 
-class RefCountedInsideLambdaChecker : public MatchFinder::MatchCallback {
+class RefCountedInsideLambdaChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  RefCountedInsideLambdaChecker(StringRef CheckName,
+                                ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/ScopeChecker.cpp
+++ b/build/clang-plugin/ScopeChecker.cpp
@@ -1,23 +1,23 @@
 /* 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 "ScopeChecker.h"
 #include "CustomMatchers.h"
 
-void ScopeChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(varDecl().bind("node"), this);
-  AstMatcher.addMatcher(cxxNewExpr().bind("node"), this);
-  AstMatcher.addMatcher(materializeTemporaryExpr().bind("node"), this);
-  AstMatcher.addMatcher(
+void ScopeChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(varDecl().bind("node"), this);
+  AstMatcher->addMatcher(cxxNewExpr().bind("node"), this);
+  AstMatcher->addMatcher(materializeTemporaryExpr().bind("node"), this);
+  AstMatcher->addMatcher(
       callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
       this);
-  AstMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), this);
+  AstMatcher->addMatcher(parmVarDecl().bind("parm_vardecl"), this);
 }
 
 // These enum variants determine whether an allocation has occured in the code.
 enum AllocationVariety {
   AV_None,
   AV_Global,
   AV_Automatic,
   AV_Temporary,
@@ -26,20 +26,18 @@ enum AllocationVariety {
 
 // XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it
 // probably will be used at some point in the future, in order to produce better
 // error messages.
 typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *>
     AutomaticTemporaryMap;
 AutomaticTemporaryMap AutomaticTemporaries;
 
-void ScopeChecker::run(
+void ScopeChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-
   // There are a variety of different reasons why something could be allocated
   AllocationVariety Variety = AV_None;
   SourceLocation Loc;
   QualType T;
 
   if (const ParmVarDecl *D =
           Result.Nodes.getNodeAs<ParmVarDecl>("parm_vardecl")) {
     if (D->hasUnparsedDefaultArg() || D->hasUninstantiatedDefaultArg()) {
@@ -117,58 +115,57 @@ void ScopeChecker::run(
       // This will always allocate on the heap, as the heapAllocator() check
       // was made in the matcher
       Variety = AV_Heap;
       Loc = E->getLocStart();
     }
   }
 
   // Error messages for incorrect allocations.
-  unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
-  unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "variable of type %0 only valid as global");
-  unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "variable of type %0 only valid on the heap");
-  unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
-  unsigned NonTemporaryID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error, "variable of type %0 is not valid in a temporary");
+  const char* Stack =
+      "variable of type %0 only valid on the stack";
+  const char* Global =
+      "variable of type %0 only valid as global";
+  const char* Heap =
+      "variable of type %0 only valid on the heap";
+  const char* NonHeap =
+      "variable of type %0 is not valid on the heap";
+  const char* NonTemporary =
+      "variable of type %0 is not valid in a temporary";
 
-  unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note,
-      "value incorrectly allocated in an automatic variable");
-  unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "value incorrectly allocated in a global variable");
-  unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "value incorrectly allocated on the heap");
-  unsigned TemporaryNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Note, "value incorrectly allocated in a temporary");
+  const char* StackNote =
+      "value incorrectly allocated in an automatic variable";
+  const char* GlobalNote =
+      "value incorrectly allocated in a global variable";
+  const char* HeapNote =
+      "value incorrectly allocated on the heap";
+  const char* TemporaryNote =
+      "value incorrectly allocated in a temporary";
 
   // Report errors depending on the annotations on the input types.
   switch (Variety) {
   case AV_None:
     return;
 
   case AV_Global:
-    StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, GlobalNoteID);
-    HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, GlobalNoteID);
+    StackClass.reportErrorIfPresent(*this, T, Loc, Stack, GlobalNote);
+    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, GlobalNote);
     break;
 
   case AV_Automatic:
-    GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, StackNoteID);
-    HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, StackNoteID);
+    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, StackNote);
+    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, StackNote);
     break;
 
   case AV_Temporary:
-    GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID);
-    HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID);
-    NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc, NonTemporaryID,
-                                           TemporaryNoteID);
+    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, TemporaryNote);
+    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, TemporaryNote);
+    NonTemporaryClass.reportErrorIfPresent(*this, T, Loc, NonTemporary,
+                                           TemporaryNote);
     break;
 
   case AV_Heap:
-    GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, HeapNoteID);
-    StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, HeapNoteID);
-    NonHeapClass.reportErrorIfPresent(Diag, T, Loc, NonHeapID, HeapNoteID);
+    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, HeapNote);
+    StackClass.reportErrorIfPresent(*this, T, Loc, Stack, HeapNote);
+    NonHeapClass.reportErrorIfPresent(*this, T, Loc, NonHeap, HeapNote);
     break;
   }
 }
--- a/build/clang-plugin/ScopeChecker.h
+++ b/build/clang-plugin/ScopeChecker.h
@@ -2,15 +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/. */
 
 #ifndef ScopeChecker_h__
 #define ScopeChecker_h__
 
 #include "plugin.h"
 
-class ScopeChecker : public MatchFinder::MatchCallback {
+class ScopeChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  ScopeChecker(StringRef CheckName, ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/SprintfLiteralChecker.cpp
+++ b/build/clang-plugin/SprintfLiteralChecker.cpp
@@ -1,41 +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/. */
 
 #include "SprintfLiteralChecker.h"
 #include "CustomMatchers.h"
 
-void SprintfLiteralChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(
+void SprintfLiteralChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(
       callExpr(isSnprintfLikeFunc(),
         allOf(hasArgument(0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
                              anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(declRefExpr().bind("size")))),
                                    hasArgument(1, integerLiteral().bind("immediate")),
                                    hasArgument(1, declRefExpr(to(varDecl(hasType(isConstQualified()),
                                                                          hasInitializer(integerLiteral().bind("constant")))))))))
         .bind("funcCall"),
       this
   );
 }
 
-void SprintfLiteralChecker::run(
+void SprintfLiteralChecker::check(
     const MatchFinder::MatchResult &Result) {
   if (!Result.Context->getLangOpts().CPlusPlus) {
     // SprintfLiteral is not usable in C, so there is no point in issuing these
     // warnings.
     return;
   }
 
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "Use %1 instead of %0 when writing into a character array.");
-  unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Note, "This will prevent passing in the wrong size to %0 accidentally.");
+  const char* Error =
+    "Use %1 instead of %0 when writing into a character array.";
+  const char* Note =
+    "This will prevent passing in the wrong size to %0 accidentally.";
 
   const CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall");
 
   StringRef Name = D->getDirectCallee()->getName();
   const char *Replacement;
   if (Name == "snprintf") {
     Replacement = "SprintfLiteral";
   } else {
@@ -46,29 +45,29 @@ void SprintfLiteralChecker::run(
   const DeclRefExpr *Buffer = Result.Nodes.getNodeAs<DeclRefExpr>("buffer");
   const DeclRefExpr *Size = Result.Nodes.getNodeAs<DeclRefExpr>("size");
   if (Size) {
     // Match calls like snprintf(x, sizeof(x), ...).
     if (Buffer->getFoundDecl() != Size->getFoundDecl()) {
       return;
     }
 
-    Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement;
-    Diag.Report(D->getLocStart(), NoteID) << Name;
+    diag(D->getLocStart(), Error, DiagnosticIDs::Error) << Name << Replacement;
+    diag(D->getLocStart(), Note, DiagnosticIDs::Note) << Name;
     return;
   }
 
   const QualType QType = Buffer->getType();
   const ConstantArrayType *Type = dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull());
   if (Type) {
     // Match calls like snprintf(x, 100, ...), where x is int[100];
     const IntegerLiteral *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
     if (!Literal) {
       // Match calls like: const int y = 100; snprintf(x, y, ...);
       Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
     }
 
     if (Type->getSize().ule(Literal->getValue())) {
-      Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement;
-      Diag.Report(D->getLocStart(), NoteID) << Name;
+      diag(D->getLocStart(), Error, DiagnosticIDs::Error) << Name << Replacement;
+      diag(D->getLocStart(), Note, DiagnosticIDs::Note) << Name;
     }
   }
 }
--- a/build/clang-plugin/SprintfLiteralChecker.h
+++ b/build/clang-plugin/SprintfLiteralChecker.h
@@ -2,15 +2,18 @@
  * 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 SprintfLiteralChecker_h__
 #define SprintfLiteralChecker_h__
 
 #include "plugin.h"
 
-class SprintfLiteralChecker : public MatchFinder::MatchCallback {
+class SprintfLiteralChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  SprintfLiteralChecker(StringRef CheckName,
+                        ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
--- a/build/clang-plugin/TrivialCtorDtorChecker.cpp
+++ b/build/clang-plugin/TrivialCtorDtorChecker.cpp
@@ -1,29 +1,27 @@
 /* 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 "TrivialCtorDtorChecker.h"
 #include "CustomMatchers.h"
 
-void TrivialCtorDtorChecker::registerMatcher(MatchFinder& AstMatcher) {
-  AstMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"),
-                        this);
+void TrivialCtorDtorChecker::registerMatchers(MatchFinder* AstMatcher) {
+  AstMatcher->addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"),
+                         this);
 }
 
-void TrivialCtorDtorChecker::run(
+void TrivialCtorDtorChecker::check(
     const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
-      DiagnosticIDs::Error,
-      "class %0 must have trivial constructors and destructors");
+  const char* Error =
+      "class %0 must have trivial constructors and destructors";
   const CXXRecordDecl *Node = Result.Nodes.getNodeAs<CXXRecordDecl>("node");
 
   // We need to accept non-constexpr trivial constructors as well. This occurs
   // when a struct contains pod members, which will not be initialized. As
   // constexpr values are initialized, the constructor is non-constexpr.
   bool BadCtor = !(Node->hasConstexprDefaultConstructor() ||
                    Node->hasTrivialDefaultConstructor());
   bool BadDtor = !Node->hasTrivialDestructor();
   if (BadCtor || BadDtor)
-    Diag.Report(Node->getLocStart(), ErrorID) << Node;
+    diag(Node->getLocStart(), Error, DiagnosticIDs::Error) << Node;
 }
--- a/build/clang-plugin/TrivialCtorDtorChecker.h
+++ b/build/clang-plugin/TrivialCtorDtorChecker.h
@@ -2,15 +2,18 @@
  * 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 TrivialCtorDtorChecker_h__
 #define TrivialCtorDtorChecker_h__
 
 #include "plugin.h"
 
-class TrivialCtorDtorChecker : public MatchFinder::MatchCallback {
+class TrivialCtorDtorChecker : public BaseCheck {
 public:
-  void registerMatcher(MatchFinder& AstMatcher);
-  virtual void run(const MatchFinder::MatchResult &Result);
+  TrivialCtorDtorChecker(StringRef CheckName,
+                         ContextType *Context = nullptr)
+    : BaseCheck(CheckName, Context) {}
+  void registerMatchers(MatchFinder* AstMatcher) override;
+  void check(const MatchFinder::MatchResult &Result) override;
 };
 
 #endif
new file mode 100755
--- /dev/null
+++ b/build/clang-plugin/import_mozilla_checks.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python2.7
+# 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/.
+
+import os
+import re
+import sys
+import glob
+import shutil
+import errno
+
+
+def copy_dir_contents(src, dest):
+    for f in glob.glob("%s/*" % src):
+        try:
+            destname = "%s/%s" % (dest, os.path.basename(f))
+            if os.path.isdir(f):
+                shutil.copytree(f, destname)
+            else:
+                shutil.copy2(f, destname)
+        except OSError as e:
+            if e.errno == errno.ENOTDIR:
+                shutil.copy2(f, destname)
+            elif e.errno == errno.EEXIST:
+                if os.path.isdir(f):
+                    copy_dir_contents(f, destname)
+                else:
+                    os.remove(destname)
+                    shutil.copy2(f, destname)
+            else:
+                raise Exception('Directory not copied. Error: %s' % e)
+
+def write_cmake(module_path):
+  names = map(lambda f: '  ' + os.path.basename(f),
+              glob.glob("%s/*.cpp" % module_path))
+  with open(os.path.join(module_path, 'CMakeLists.txt'), 'wb') as f:
+    f.write("""set(LLVM_LINK_COMPONENTS support)
+
+add_definitions( -DCLANG_TIDY )
+add_definitions( -DHAVE_NEW_ASTMATCHER_NAMES )
+
+add_clang_library(clangTidyMozillaModule
+%(names)s
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyReadabilityModule
+  clangTidyUtils
+  )""" % {'names': "\n".join(names)})
+
+def add_item_to_cmake_section(cmake_path, section, library):
+  with open(cmake_path, 'r') as f:
+    lines = f.readlines()
+  f.close()
+
+  libs = []
+  seen_target_libs = False
+  for line in lines:
+    if line.find(section) > -1:
+      seen_target_libs = True
+    elif seen_target_libs:
+      if line.find(')') > -1:
+        break
+      else:
+        libs.append(line.strip())
+  libs.append(library)
+  libs = sorted(libs, key = lambda s: s.lower())
+
+  with open(cmake_path, 'wb') as f:
+    seen_target_libs = False
+    for line in lines:
+      if line.find(section) > -1:
+        seen_target_libs = True
+        f.write(line)
+        f.writelines(map(lambda p: '  ' + p + '\n', libs))
+        continue
+      elif seen_target_libs:
+        if line.find(')') > -1:
+          seen_target_libs = False
+        else:
+          continue
+      f.write(line)
+
+  f.close()
+
+
+def do_import(mozilla_path, clang_tidy_path):
+  module = 'mozilla'
+  module_path = os.path.join(clang_tidy_path, module)
+  if not os.path.isdir(module_path):
+      os.mkdir(module_path)
+
+  copy_dir_contents(mozilla_path, module_path)
+  write_cmake(module_path)
+  add_item_to_cmake_section(os.path.join(module_path, '..', 'plugin',
+                                         'CMakeLists.txt'),
+                            'LINK_LIBS', 'clangTidyMozillaModule')
+  add_item_to_cmake_section(os.path.join(module_path, '..', 'tool',
+                                         'CMakeLists.txt'),
+                            'target_link_libraries', 'clangTidyMozillaModule')
+  with open(os.path.join(module_path, '..', 'CMakeLists.txt'), 'a') as f:
+    f.write('add_subdirectory(%s)\n' % module)
+  with open(os.path.join(module_path, '..', 'tool', 'ClangTidyMain.cpp'), 'a') as f:
+    f.write('''
+// This anchor is used to force the linker to link the MozillaModule.
+extern volatile int MozillaModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MozillaModuleAnchorDestination =
+          MozillaModuleAnchorSource;
+''')
+
+def main():
+  if len(sys.argv) != 3:
+    print """\
+Usage: import_mozilla_checks.py <mozilla-clang-plugin-path> <clang-tidy-path>
+Imports the Mozilla static analysis checks into a clang-tidy source tree.
+"""
+
+    return
+
+  mozilla_path = sys.argv[1]
+  if not os.path.isdir(mozilla_path):
+      print "Invalid path to mozilla clang plugin"
+
+  clang_tidy_path = sys.argv[2]
+  if not os.path.isdir(mozilla_path):
+      print "Invalid path to clang-tidy source directory"
+
+  do_import(mozilla_path, clang_tidy_path)
+
+
+if __name__ == '__main__':
+  main()
--- a/build/clang-plugin/plugin.h
+++ b/build/clang-plugin/plugin.h
@@ -48,8 +48,19 @@ typedef ASTConsumer *ASTConsumerPtr;
 #define hasIgnoringParenImpCasts(x) has(x)
 #else
 // Before clang 3.9 "has" would behave like has(ignoringParenImpCasts(x)),
 // however doing that explicitly would not compile.
 #define hasIgnoringParenImpCasts(x) has(ignoringParenImpCasts(x))
 #endif
 
 #endif
+
+// In order to support running our checks using clang-tidy, we implement a source
+// compatible base check class called BaseCheck, and we use the preprocessor to
+// decide which base class to pick.
+#ifdef CLANG_TIDY
+#include "../ClangTidy.h"
+typedef clang::tidy::ClangTidyCheck BaseCheck;
+typedef clang::tidy::ClangTidyContext ContextType;
+#else
+#include "BaseCheck.h"
+#endif