summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/clang-plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'build/clang-plugin/clang-plugin.cpp')
-rw-r--r--build/clang-plugin/clang-plugin.cpp2331
1 files changed, 2331 insertions, 0 deletions
diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp
new file mode 100644
index 000000000..f420d3e8b
--- /dev/null
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -0,0 +1,2331 @@
+/* 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/. */
+
+/* This file respects the LLVM coding standard described at
+ * http://llvm.org/docs/CodingStandards.html */
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Version.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include <memory>
+#include <iterator>
+
+#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
+
+using namespace llvm;
+using namespace clang;
+
+#if CLANG_VERSION_FULL >= 306
+typedef std::unique_ptr<ASTConsumer> ASTConsumerPtr;
+#else
+typedef ASTConsumer *ASTConsumerPtr;
+#endif
+
+#ifndef HAVE_NEW_ASTMATCHER_NAMES
+// In clang 3.8, a number of AST matchers were renamed to better match the
+// respective AST node. We use the new names, and #define them to the old
+// ones for compatibility with older versions.
+#define cxxConstructExpr constructExpr
+#define cxxConstructorDecl constructorDecl
+#define cxxMethodDecl methodDecl
+#define cxxNewExpr newExpr
+#define cxxRecordDecl recordDecl
+#endif
+
+#ifndef HAS_ACCEPTS_IGNORINGPARENIMPCASTS
+#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
+
+// Check if the given expression contains an assignment expression.
+// This can either take the form of a Binary Operator or a
+// Overloaded Operator Call.
+bool hasSideEffectAssignment(const Expr *Expression) {
+ if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
+ auto BinOp = OpCallExpr->getOperator();
+ if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) {
+ return true;
+ }
+ } else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) {
+ if (BinOpExpr->isAssignmentOp()) {
+ return true;
+ }
+ }
+
+ // Recurse to children.
+ for (const Stmt *SubStmt : Expression->children()) {
+ auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
+ if (ChildExpr && hasSideEffectAssignment(ChildExpr)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+
+using namespace clang::ast_matchers;
+class DiagnosticsMatcher {
+public:
+ DiagnosticsMatcher();
+
+ ASTConsumerPtr makeASTConsumer() { return AstMatcher.newASTConsumer(); }
+
+private:
+ class ScopeChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class ArithmeticArgChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class TrivialCtorDtorChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NaNExprChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NoAddRefReleaseOnReturnChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class RefCountedInsideLambdaChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ void emitDiagnostics(SourceLocation Loc, StringRef Name, QualType Type);
+
+ private:
+ class ThisVisitor : public RecursiveASTVisitor<ThisVisitor> {
+ public:
+ explicit ThisVisitor(RefCountedInsideLambdaChecker& Checker)
+ : Checker(Checker) {}
+
+ bool VisitCXXThisExpr(CXXThisExpr *This);
+ private:
+ RefCountedInsideLambdaChecker& Checker;
+ };
+
+ ASTContext *Context;
+ };
+
+ class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NoDuplicateRefCntMemberChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NonMemMovableMemberChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class ExplicitImplicitChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NoAutoTypeChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NoExplicitMoveConstructorChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class AssertAssignmentChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class KungFuDeathGripChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class SprintfLiteralChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class OverrideBaseCallChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ 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);
+ };
+
+/*
+ * 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 {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ class NonParamInsideFunctionDeclChecker : public MatchFinder::MatchCallback {
+ public:
+ virtual void run(const MatchFinder::MatchResult &Result);
+ };
+
+ 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 AssertAttribution;
+ KungFuDeathGripChecker KungFuDeathGrip;
+ SprintfLiteralChecker SprintfLiteral;
+ OverrideBaseCallChecker OverrideBaseCall;
+ OverrideBaseCallUsageChecker OverrideBaseCallUsage;
+ NonParamInsideFunctionDeclChecker NonParamInsideFunctionDecl;
+ MatchFinder AstMatcher;
+};
+
+namespace {
+
+std::string getDeclarationNamespace(const Decl *Declaration) {
+ const DeclContext *DC =
+ Declaration->getDeclContext()->getEnclosingNamespaceContext();
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
+ if (!ND) {
+ return "";
+ }
+
+ while (const DeclContext *ParentDC = ND->getParent()) {
+ if (!isa<NamespaceDecl>(ParentDC)) {
+ break;
+ }
+ ND = cast<NamespaceDecl>(ParentDC);
+ }
+
+ const auto &Name = ND->getName();
+ return Name;
+}
+
+bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) {
+ std::string Name = getDeclarationNamespace(Declaration);
+ if (Name == "") {
+ return false;
+ }
+
+ return Name == "std" || // standard C++ lib
+ Name == "__gnu_cxx" || // gnu C++ lib
+ Name == "boost" || // boost
+ Name == "webrtc" || // upstream webrtc
+ Name == "rtc" || // upstream webrtc 'base' package
+ Name.substr(0, 4) == "icu_" || // icu
+ Name == "google" || // protobuf
+ Name == "google_breakpad" || // breakpad
+ Name == "soundtouch" || // libsoundtouch
+ Name == "stagefright" || // libstagefright
+ Name == "MacFileUtilities" || // MacFileUtilities
+ Name == "dwarf2reader" || // dwarf2reader
+ Name == "arm_ex_to_module" || // arm_ex_to_module
+ Name == "testing" || // gtest
+ Name == "Json"; // jsoncpp
+}
+
+bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) {
+ std::string Name = getDeclarationNamespace(Declaration);
+ if (Name == "") {
+ return false;
+ }
+
+ return Name == "std" || // standard C++ lib
+ Name == "__gnu_cxx" || // gnu C++ lib
+ Name == "google_breakpad" || // breakpad
+ Name == "testing"; // gtest
+}
+
+bool isIgnoredPathForImplicitCtor(const Decl *Declaration) {
+ SourceLocation Loc = Declaration->getLocation();
+ const SourceManager &SM = Declaration->getASTContext().getSourceManager();
+ SmallString<1024> FileName = SM.getFilename(Loc);
+ llvm::sys::fs::make_absolute(FileName);
+ llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
+ End = llvm::sys::path::rend(FileName);
+ for (; Begin != End; ++Begin) {
+ if (Begin->compare_lower(StringRef("skia")) == 0 ||
+ Begin->compare_lower(StringRef("angle")) == 0 ||
+ Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
+ Begin->compare_lower(StringRef("hunspell")) == 0 ||
+ Begin->compare_lower(StringRef("scoped_ptr.h")) == 0 ||
+ Begin->compare_lower(StringRef("graphite2")) == 0 ||
+ Begin->compare_lower(StringRef("icu")) == 0) {
+ return true;
+ }
+ if (Begin->compare_lower(StringRef("chromium")) == 0) {
+ // Ignore security/sandbox/chromium but not ipc/chromium.
+ ++Begin;
+ return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
+ }
+ }
+ return false;
+}
+
+bool isIgnoredPathForImplicitConversion(const Decl *Declaration) {
+ Declaration = Declaration->getCanonicalDecl();
+ SourceLocation Loc = Declaration->getLocation();
+ const SourceManager &SM = Declaration->getASTContext().getSourceManager();
+ SmallString<1024> FileName = SM.getFilename(Loc);
+ llvm::sys::fs::make_absolute(FileName);
+ llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
+ End = llvm::sys::path::rend(FileName);
+ for (; Begin != End; ++Begin) {
+ if (Begin->compare_lower(StringRef("graphite2")) == 0) {
+ return true;
+ }
+ if (Begin->compare_lower(StringRef("chromium")) == 0) {
+ // Ignore security/sandbox/chromium but not ipc/chromium.
+ ++Begin;
+ return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
+ }
+ }
+ return false;
+}
+
+bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceManager &SM) {
+ SourceLocation Loc = Call->getLocStart();
+ SmallString<1024> FileName = SM.getFilename(Loc);
+ llvm::sys::fs::make_absolute(FileName);
+ llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
+ End = llvm::sys::path::rend(FileName);
+ for (; Begin != End; ++Begin) {
+ if (Begin->compare_lower(StringRef("angle")) == 0 ||
+ Begin->compare_lower(StringRef("chromium")) == 0 ||
+ Begin->compare_lower(StringRef("crashreporter")) == 0 ||
+ Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
+ Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
+ Begin->compare_lower(StringRef("libstagefright")) == 0 ||
+ Begin->compare_lower(StringRef("mtransport")) == 0 ||
+ Begin->compare_lower(StringRef("protobuf")) == 0 ||
+ Begin->compare_lower(StringRef("skia")) == 0 ||
+ // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
+ Begin->compare_lower(StringRef("testing")) == 0) {
+ return true;
+ }
+ if (Begin->compare_lower(StringRef("webrtc")) == 0) {
+ // Ignore trunk/webrtc, but not media/webrtc
+ ++Begin;
+ return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0;
+ }
+ }
+ return false;
+}
+
+bool isInterestingDeclForImplicitConversion(const Decl *Declaration) {
+ return !isInIgnoredNamespaceForImplicitConversion(Declaration) &&
+ !isIgnoredPathForImplicitConversion(Declaration);
+}
+
+bool isIgnoredExprForMustUse(const Expr *E) {
+ if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+ switch (OpCall->getOperator()) {
+ case OO_Equal:
+ case OO_PlusEqual:
+ case OO_MinusEqual:
+ case OO_StarEqual:
+ case OO_SlashEqual:
+ case OO_PercentEqual:
+ case OO_CaretEqual:
+ case OO_AmpEqual:
+ case OO_PipeEqual:
+ case OO_LessLessEqual:
+ case OO_GreaterGreaterEqual:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
+ return Op->isAssignmentOp();
+ }
+
+ return false;
+}
+
+template<typename T>
+StringRef getNameChecked(const T& D) {
+ return D->getIdentifier() ? D->getName() : "";
+}
+
+bool typeIsRefPtr(QualType Q) {
+ CXXRecordDecl *D = Q->getAsCXXRecordDecl();
+ if (!D || !D->getIdentifier()) {
+ return false;
+ }
+
+ StringRef name = D->getName();
+ if (name == "RefPtr" || name == "nsCOMPtr") {
+ return true;
+ }
+ return false;
+}
+
+// The method defined in clang for ignoring implicit nodes doesn't work with
+// some AST trees. To get around this, we define our own implementation of
+// IgnoreImplicit.
+const Stmt *IgnoreImplicit(const Stmt *s) {
+ while (true) {
+ if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
+ s = ewc->getSubExpr();
+ } else if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
+ s = mte->GetTemporaryExpr();
+ } else if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
+ s = bte->getSubExpr();
+ } else if (auto *ice = dyn_cast<ImplicitCastExpr>(s)) {
+ s = ice->getSubExpr();
+ } else {
+ break;
+ }
+ }
+
+ return s;
+}
+
+const Expr *IgnoreImplicit(const Expr *e) {
+ return cast<Expr>(IgnoreImplicit(static_cast<const Stmt *>(e)));
+}
+}
+
+class CustomTypeAnnotation {
+ enum ReasonKind {
+ RK_None,
+ RK_Direct,
+ RK_ArrayElement,
+ RK_BaseClass,
+ RK_Field,
+ RK_TemplateInherited,
+ };
+ struct AnnotationReason {
+ QualType Type;
+ ReasonKind Kind;
+ const FieldDecl *Field;
+
+ bool valid() const { return Kind != RK_None; }
+ };
+ typedef DenseMap<void *, AnnotationReason> ReasonCache;
+
+ const char *Spelling;
+ const char *Pretty;
+ ReasonCache Cache;
+
+public:
+ CustomTypeAnnotation(const char *Spelling, const char *Pretty)
+ : Spelling(Spelling), Pretty(Pretty){};
+
+ 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 reportErrorIfPresent(DiagnosticsEngine &Diag, QualType T,
+ SourceLocation Loc, unsigned ErrorID,
+ unsigned NoteID) {
+ if (hasEffectiveAnnotation(T)) {
+ Diag.Report(Loc, ErrorID) << T;
+ Diag.Report(Loc, NoteID);
+ dumpAnnotationReason(Diag, T, Loc);
+ }
+ }
+
+private:
+ bool hasLiteralAnnotation(QualType T) const;
+ AnnotationReason directAnnotationReason(QualType T);
+ AnnotationReason tmplArgAnnotationReason(ArrayRef<TemplateArgument> Args);
+
+protected:
+ // Allow subclasses to apply annotations to external code:
+ virtual bool hasFakeAnnotation(const TagDecl *D) const { return false; }
+};
+
+static CustomTypeAnnotation StackClass =
+ CustomTypeAnnotation("moz_stack_class", "stack");
+static CustomTypeAnnotation GlobalClass =
+ CustomTypeAnnotation("moz_global_class", "global");
+static CustomTypeAnnotation NonHeapClass =
+ CustomTypeAnnotation("moz_nonheap_class", "non-heap");
+static CustomTypeAnnotation HeapClass =
+ CustomTypeAnnotation("moz_heap_class", "heap");
+static CustomTypeAnnotation NonTemporaryClass =
+ CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
+static CustomTypeAnnotation MustUse =
+ CustomTypeAnnotation("moz_must_use_type", "must-use");
+static CustomTypeAnnotation NonParam =
+ CustomTypeAnnotation("moz_non_param", "non-param");
+
+class MemMoveAnnotation final : public CustomTypeAnnotation {
+public:
+ MemMoveAnnotation()
+ : CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {}
+
+ virtual ~MemMoveAnnotation() {}
+
+protected:
+ bool hasFakeAnnotation(const TagDecl *D) const override {
+ // Annotate everything in ::std, with a few exceptions; see bug
+ // 1201314 for discussion.
+ if (getDeclarationNamespace(D) == "std") {
+ // This doesn't check that it's really ::std::pair and not
+ // ::std::something_else::pair, but should be good enough.
+ StringRef Name = getNameChecked(D);
+ if (Name == "pair" || Name == "atomic" || Name == "__atomic_base") {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+};
+
+static MemMoveAnnotation NonMemMovable = MemMoveAnnotation();
+
+class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
+ DiagnosticsEngine &Diag;
+ const CompilerInstance &CI;
+ DiagnosticsMatcher Matcher;
+
+public:
+ MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {}
+
+ ASTConsumerPtr getOtherConsumer() { return Matcher.makeASTConsumer(); }
+
+ virtual void HandleTranslationUnit(ASTContext &Ctx) override {
+ TraverseDecl(Ctx.getTranslationUnitDecl());
+ }
+
+ static bool hasCustomAnnotation(const Decl *D, const char *Spelling) {
+ iterator_range<specific_attr_iterator<AnnotateAttr>> Attrs =
+ D->specific_attrs<AnnotateAttr>();
+
+ for (AnnotateAttr *Attr : Attrs) {
+ if (Attr->getAnnotation() == Spelling) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void handleUnusedExprResult(const Stmt *Statement) {
+ const Expr *E = dyn_cast_or_null<Expr>(Statement);
+ if (E) {
+ E = E->IgnoreImplicit(); // Ignore ExprWithCleanup etc. implicit wrappers
+ QualType T = E->getType();
+ if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) {
+ unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Error, "Unused value of must-use type %0");
+
+ Diag.Report(E->getLocStart(), ErrorID) << T;
+ MustUse.dumpAnnotationReason(Diag, T, E->getLocStart());
+ }
+ }
+ }
+
+ bool VisitCXXRecordDecl(CXXRecordDecl *D) {
+ // We need definitions, not declarations
+ if (!D->isThisDeclarationADefinition())
+ return true;
+
+ // Look through all of our immediate bases to find methods that need to be
+ // overridden
+ typedef std::vector<CXXMethodDecl *> OverridesVector;
+ OverridesVector MustOverrides;
+ for (CXXRecordDecl::base_class_iterator Base = D->bases_begin(),
+ E = D->bases_end();
+ Base != E; ++Base) {
+ // The base is either a class (CXXRecordDecl) or it's a templated class...
+ CXXRecordDecl *Parent = Base->getType()
+ .getDesugaredType(D->getASTContext())
+ ->getAsCXXRecordDecl();
+ // The parent might not be resolved to a type yet. In this case, we can't
+ // do any checking here. For complete correctness, we should visit
+ // template instantiations, but this case is likely to be rare, so we will
+ // ignore it until it becomes important.
+ if (!Parent) {
+ continue;
+ }
+ Parent = Parent->getDefinition();
+ for (CXXRecordDecl::method_iterator M = Parent->method_begin();
+ M != Parent->method_end(); ++M) {
+ if (hasCustomAnnotation(*M, "moz_must_override"))
+ MustOverrides.push_back(*M);
+ }
+ }
+
+ for (OverridesVector::iterator It = MustOverrides.begin();
+ It != MustOverrides.end(); ++It) {
+ bool Overridden = false;
+ for (CXXRecordDecl::method_iterator M = D->method_begin();
+ !Overridden && M != D->method_end(); ++M) {
+ // The way that Clang checks if a method M overrides its parent method
+ // is if the method has the same name but would not overload.
+ if (getNameChecked(M) == getNameChecked(*It) &&
+ !CI.getSema().IsOverload(*M, (*It), false)) {
+ Overridden = true;
+ break;
+ }
+ }
+ if (!Overridden) {
+ unsigned OverrideID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Error, "%0 must override %1");
+ unsigned OverrideNote = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Note, "function to override is here");
+ Diag.Report(D->getLocation(), OverrideID) << D->getDeclName()
+ << (*It)->getDeclName();
+ Diag.Report((*It)->getLocation(), OverrideNote);
+ }
+ }
+
+ return true;
+ }
+
+ bool VisitSwitchCase(SwitchCase *Statement) {
+ handleUnusedExprResult(Statement->getSubStmt());
+ return true;
+ }
+ bool VisitCompoundStmt(CompoundStmt *Statement) {
+ for (CompoundStmt::body_iterator It = Statement->body_begin(),
+ E = Statement->body_end();
+ It != E; ++It) {
+ handleUnusedExprResult(*It);
+ }
+ return true;
+ }
+ bool VisitIfStmt(IfStmt *Statement) {
+ handleUnusedExprResult(Statement->getThen());
+ handleUnusedExprResult(Statement->getElse());
+ return true;
+ }
+ bool VisitWhileStmt(WhileStmt *Statement) {
+ handleUnusedExprResult(Statement->getBody());
+ return true;
+ }
+ bool VisitDoStmt(DoStmt *Statement) {
+ handleUnusedExprResult(Statement->getBody());
+ return true;
+ }
+ bool VisitForStmt(ForStmt *Statement) {
+ handleUnusedExprResult(Statement->getBody());
+ handleUnusedExprResult(Statement->getInit());
+ handleUnusedExprResult(Statement->getInc());
+ return true;
+ }
+ bool VisitBinComma(BinaryOperator *Op) {
+ handleUnusedExprResult(Op->getLHS());
+ return true;
+ }
+};
+
+/// A cached data of whether classes are refcounted or not.
+typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>>
+ RefCountedMap;
+RefCountedMap RefCountedClasses;
+
+bool classHasAddRefRelease(const CXXRecordDecl *D) {
+ const RefCountedMap::iterator &It = RefCountedClasses.find(D);
+ if (It != RefCountedClasses.end()) {
+ return It->second.second;
+ }
+
+ bool SeenAddRef = false;
+ bool SeenRelease = false;
+ for (CXXRecordDecl::method_iterator Method = D->method_begin();
+ Method != D->method_end(); ++Method) {
+ const auto &Name = getNameChecked(Method);
+ if (Name == "AddRef") {
+ SeenAddRef = true;
+ } else if (Name == "Release") {
+ SeenRelease = true;
+ }
+ }
+ RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease);
+ return SeenAddRef && SeenRelease;
+}
+
+bool isClassRefCounted(QualType T);
+
+bool isClassRefCounted(const CXXRecordDecl *D) {
+ // Normalize so that D points to the definition if it exists.
+ if (!D->hasDefinition())
+ return false;
+ D = D->getDefinition();
+ // Base class: anyone with AddRef/Release is obviously a refcounted class.
+ if (classHasAddRefRelease(D))
+ return true;
+
+ // Look through all base cases to figure out if the parent is a refcounted
+ // class.
+ for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin();
+ Base != D->bases_end(); ++Base) {
+ bool Super = isClassRefCounted(Base->getType());
+ if (Super) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isClassRefCounted(QualType T) {
+ while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
+ T = ArrTy->getElementType();
+ CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
+ return Clazz ? isClassRefCounted(Clazz) : false;
+}
+
+template <class T> bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
+ auto &SourceManager = AC.getSourceManager();
+ auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
+ if (ExpansionLoc.isInvalid()) {
+ return false;
+ }
+ return SourceManager.isInSystemHeader(ExpansionLoc);
+}
+
+const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) {
+ for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end();
+ Field != E; ++Field) {
+ if (getNameChecked(Field) == "mRefCnt") {
+ return *Field;
+ }
+ }
+ return 0;
+}
+
+const FieldDecl *getBaseRefCntMember(QualType T);
+
+const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
+ const FieldDecl *RefCntMember = getClassRefCntMember(D);
+ if (RefCntMember && isClassRefCounted(D)) {
+ return RefCntMember;
+ }
+
+ for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(),
+ E = D->bases_end();
+ Base != E; ++Base) {
+ RefCntMember = getBaseRefCntMember(Base->getType());
+ if (RefCntMember) {
+ return RefCntMember;
+ }
+ }
+ return 0;
+}
+
+const FieldDecl *getBaseRefCntMember(QualType T) {
+ while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
+ T = ArrTy->getElementType();
+ CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
+ return Clazz ? getBaseRefCntMember(Clazz) : 0;
+}
+
+bool typeHasVTable(QualType T) {
+ while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
+ T = ArrTy->getElementType();
+ CXXRecordDecl *Offender = T->getAsCXXRecordDecl();
+ return Offender && Offender->hasDefinition() && Offender->isDynamicClass();
+}
+}
+
+namespace clang {
+namespace ast_matchers {
+
+/// This matcher will match any function declaration that is declared as a heap
+/// allocator.
+AST_MATCHER(FunctionDecl, heapAllocator) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
+}
+
+/// This matcher will match any declaration that is marked as not accepting
+/// arithmetic expressions in its arguments.
+AST_MATCHER(Decl, noArithmeticExprInArgs) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg");
+}
+
+/// This matcher will match any C++ class that is marked as having a trivial
+/// constructor and destructor.
+AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
+}
+
+/// This matcher will match any function declaration that is marked to prohibit
+/// calling AddRef or Release on its return value.
+AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
+ return MozChecker::hasCustomAnnotation(&Node,
+ "moz_no_addref_release_on_return");
+}
+
+/// This matcher will match all arithmetic binary operators.
+AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
+ BinaryOperatorKind OpCode = Node.getOpcode();
+ return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem ||
+ OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl ||
+ OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor ||
+ OpCode == BO_Or || OpCode == BO_MulAssign || OpCode == BO_DivAssign ||
+ OpCode == BO_RemAssign || OpCode == BO_AddAssign ||
+ OpCode == BO_SubAssign || OpCode == BO_ShlAssign ||
+ OpCode == BO_ShrAssign || OpCode == BO_AndAssign ||
+ OpCode == BO_XorAssign || OpCode == BO_OrAssign;
+}
+
+/// This matcher will match all arithmetic unary operators.
+AST_MATCHER(UnaryOperator, unaryArithmeticOperator) {
+ UnaryOperatorKind OpCode = Node.getOpcode();
+ return OpCode == UO_PostInc || OpCode == UO_PostDec || OpCode == UO_PreInc ||
+ OpCode == UO_PreDec || OpCode == UO_Plus || OpCode == UO_Minus ||
+ OpCode == UO_Not;
+}
+
+/// This matcher will match == and != binary operators.
+AST_MATCHER(BinaryOperator, binaryEqualityOperator) {
+ BinaryOperatorKind OpCode = Node.getOpcode();
+ return OpCode == BO_EQ || OpCode == BO_NE;
+}
+
+/// This matcher will match floating point types.
+AST_MATCHER(QualType, isFloat) { return Node->isRealFloatingType(); }
+
+/// This matcher will match locations in system headers. This is adopted from
+/// isExpansionInSystemHeader in newer clangs, but modified in order to work
+/// with old clangs that we use on infra.
+AST_MATCHER(BinaryOperator, isInSystemHeader) {
+ return ASTIsInSystemHeader(Finder->getASTContext(), Node);
+}
+
+/// This matcher will match a list of files. These files contain
+/// known NaN-testing expressions which we would like to whitelist.
+AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) {
+ const char* whitelist[] = {
+ "SkScalar.h",
+ "json_writer.cpp"
+ };
+
+ SourceLocation Loc = Node.getOperatorLoc();
+ auto &SourceManager = Finder->getASTContext().getSourceManager();
+ SmallString<1024> FileName = SourceManager.getFilename(Loc);
+
+ for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) {
+ if (llvm::sys::path::rbegin(FileName)->equals(*itr)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// This matcher will match all accesses to AddRef or Release methods.
+AST_MATCHER(MemberExpr, isAddRefOrRelease) {
+ ValueDecl *Member = Node.getMemberDecl();
+ CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
+ if (Method) {
+ const auto &Name = getNameChecked(Method);
+ return Name == "AddRef" || Name == "Release";
+ }
+ return false;
+}
+
+/// This matcher will select classes which are refcounted.
+AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
+ return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
+}
+
+AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
+
+AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
+}
+
+/// This matcher will select classes which are non-memmovable
+AST_MATCHER(QualType, isNonMemMovable) {
+ return NonMemMovable.hasEffectiveAnnotation(Node);
+}
+
+/// This matcher will select classes which require a memmovable template arg
+AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
+}
+
+/// This matcher will select classes which require all members to be memmovable
+AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
+}
+
+AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
+ const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
+ return
+ // Skip ignored namespaces and paths
+ !isInIgnoredNamespaceForImplicitCtor(Declaration) &&
+ !isIgnoredPathForImplicitCtor(Declaration) &&
+ // We only want Converting constructors
+ Declaration->isConvertingConstructor(false) &&
+ // We don't want copy of move constructors, as those are allowed to be
+ // implicit
+ !Declaration->isCopyOrMoveConstructor() &&
+ // We don't want deleted constructors.
+ !Declaration->isDeleted();
+}
+
+// We can't call this "isImplicit" since it clashes with an existing matcher in
+// clang.
+AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) {
+ return MozChecker::hasCustomAnnotation(&Node, "moz_implicit");
+}
+
+AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
+
+AST_MATCHER(QualType, autoNonAutoableType) {
+ if (const AutoType *T = Node->getContainedAutoType()) {
+ if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) {
+ return MozChecker::hasCustomAnnotation(Rec, "moz_non_autoable");
+ }
+ }
+ return false;
+}
+
+AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
+ return Node.isExplicit() && Node.isMoveConstructor();
+}
+
+AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
+ return !Node.isUserProvided() && Node.isCopyConstructor();
+}
+
+AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) {
+ static const std::string AssertName = "MOZ_AssertAssignmentTest";
+ const FunctionDecl *Method = Node.getDirectCallee();
+
+ return Method
+ && Method->getDeclName().isIdentifier()
+ && Method->getName() == AssertName;
+}
+
+AST_MATCHER(CallExpr, isSnprintfLikeFunc) {
+ static const std::string Snprintf = "snprintf";
+ static const std::string Vsnprintf = "vsnprintf";
+ const FunctionDecl *Func = Node.getDirectCallee();
+
+ if (!Func || isa<CXXMethodDecl>(Func)) {
+ return false;
+ }
+
+ StringRef Name = getNameChecked(Func);
+ if (Name != Snprintf && Name != Vsnprintf) {
+ return false;
+ }
+
+ return !isIgnoredPathForSprintfLiteral(&Node, Finder->getASTContext().getSourceManager());
+}
+
+AST_MATCHER(CXXRecordDecl, isLambdaDecl) {
+ return Node.isLambda();
+}
+
+AST_MATCHER(QualType, isRefPtr) {
+ return typeIsRefPtr(Node);
+}
+
+AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
+ const CXXRecordDecl *Decl = Node.getCanonicalDecl();
+
+ // Must have definition and should inherit other classes
+ return Decl && Decl->hasDefinition() && Decl->getNumBases();
+}
+
+AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
+ const CXXMethodDecl *Decl = Node.getCanonicalDecl();
+ return Decl
+ && MozChecker::hasCustomAnnotation(Decl, "moz_required_base_method");
+}
+
+AST_MATCHER(CXXMethodDecl, isNonVirtual) {
+ const CXXMethodDecl *Decl = Node.getCanonicalDecl();
+ return Decl && !Decl->isVirtual();
+}
+}
+}
+
+namespace {
+
+void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag,
+ QualType T,
+ SourceLocation Loc) {
+ unsigned InheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Note,
+ "%1 is a %0 type because it inherits from a %0 type %2");
+ unsigned MemberID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Note, "%1 is a %0 type because member %2 is a %0 type %3");
+ unsigned ArrayID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Note,
+ "%1 is a %0 type because it is an array of %0 type %2");
+ unsigned TemplID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Note,
+ "%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:
+ Diag.Report(Loc, ArrayID) << Pretty << T << Reason.Type;
+ break;
+ case RK_BaseClass: {
+ const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl();
+ assert(Declaration && "This type should be a C++ class");
+
+ Diag.Report(Declaration->getLocation(), InheritsID) << Pretty << T
+ << Reason.Type;
+ break;
+ }
+ case RK_Field:
+ Diag.Report(Reason.Field->getLocation(), MemberID)
+ << Pretty << T << Reason.Field << Reason.Type;
+ break;
+ case RK_TemplateInherited: {
+ const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl();
+ assert(Declaration && "This type should be a C++ class");
+
+ Diag.Report(Declaration->getLocation(), TemplID) << 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);
+ }
+ return false;
+}
+
+CustomTypeAnnotation::AnnotationReason
+CustomTypeAnnotation::directAnnotationReason(QualType T) {
+ if (hasLiteralAnnotation(T)) {
+ AnnotationReason Reason = {T, RK_Direct, nullptr};
+ return Reason;
+ }
+
+ // Check if we have a cached answer
+ void *Key = T.getAsOpaquePtr();
+ ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr());
+ if (Cached != Cache.end()) {
+ return Cached->second;
+ }
+
+ // Check if we have a type which we can recurse into
+ if (const clang::ArrayType *Array = T->getAsArrayTypeUnsafe()) {
+ if (hasEffectiveAnnotation(Array->getElementType())) {
+ AnnotationReason Reason = {Array->getElementType(), RK_ArrayElement,
+ nullptr};
+ Cache[Key] = Reason;
+ return Reason;
+ }
+ }
+
+ // Recurse into Base classes
+ if (const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl()) {
+ if (Declaration->hasDefinition()) {
+ Declaration = Declaration->getDefinition();
+
+ for (const CXXBaseSpecifier &Base : Declaration->bases()) {
+ if (hasEffectiveAnnotation(Base.getType())) {
+ AnnotationReason Reason = {Base.getType(), RK_BaseClass, nullptr};
+ Cache[Key] = Reason;
+ return Reason;
+ }
+ }
+
+ // Recurse into members
+ for (const FieldDecl *Field : Declaration->fields()) {
+ if (hasEffectiveAnnotation(Field->getType())) {
+ AnnotationReason Reason = {Field->getType(), RK_Field, Field};
+ Cache[Key] = Reason;
+ return Reason;
+ }
+ }
+
+ // Recurse into template arguments if the annotation
+ // MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present
+ if (MozChecker::hasCustomAnnotation(
+ Declaration, "moz_inherit_type_annotations_from_template_args")) {
+ const ClassTemplateSpecializationDecl *Spec =
+ dyn_cast<ClassTemplateSpecializationDecl>(Declaration);
+ if (Spec) {
+ const TemplateArgumentList &Args = Spec->getTemplateArgs();
+
+ AnnotationReason Reason = tmplArgAnnotationReason(Args.asArray());
+ if (Reason.Kind != RK_None) {
+ Cache[Key] = Reason;
+ return Reason;
+ }
+ }
+ }
+ }
+ }
+
+ AnnotationReason Reason = {QualType(), RK_None, nullptr};
+ Cache[Key] = Reason;
+ return Reason;
+}
+
+CustomTypeAnnotation::AnnotationReason
+CustomTypeAnnotation::tmplArgAnnotationReason(ArrayRef<TemplateArgument> Args) {
+ for (const TemplateArgument &Arg : Args) {
+ if (Arg.getKind() == TemplateArgument::Type) {
+ QualType Type = Arg.getAsType();
+ if (hasEffectiveAnnotation(Type)) {
+ AnnotationReason Reason = {Type, RK_TemplateInherited, nullptr};
+ return Reason;
+ }
+ } else if (Arg.getKind() == TemplateArgument::Pack) {
+ AnnotationReason Reason = tmplArgAnnotationReason(Arg.getPackAsArray());
+ if (Reason.Kind != RK_None) {
+ return Reason;
+ }
+ }
+ }
+
+ AnnotationReason Reason = {QualType(), RK_None, nullptr};
+ return Reason;
+}
+
+bool isPlacementNew(const CXXNewExpr *Expression) {
+ // Regular new expressions aren't placement new
+ if (Expression->getNumPlacementArgs() == 0)
+ return false;
+ const FunctionDecl *Declaration = Expression->getOperatorNew();
+ if (Declaration && MozChecker::hasCustomAnnotation(Declaration,
+ "moz_heap_allocator")) {
+ return false;
+ }
+ return true;
+}
+
+DiagnosticsMatcher::DiagnosticsMatcher() {
+ AstMatcher.addMatcher(varDecl().bind("node"), &Scope);
+ AstMatcher.addMatcher(cxxNewExpr().bind("node"), &Scope);
+ AstMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &Scope);
+ AstMatcher.addMatcher(
+ callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
+ &Scope);
+ AstMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &Scope);
+
+ AstMatcher.addMatcher(
+ callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
+ anyOf(hasDescendant(
+ binaryOperator(
+ allOf(binaryArithmeticOperator(),
+ hasLHS(hasDescendant(declRefExpr())),
+ hasRHS(hasDescendant(declRefExpr()))))
+ .bind("node")),
+ hasDescendant(
+ unaryOperator(
+ allOf(unaryArithmeticOperator(),
+ hasUnaryOperand(allOf(
+ hasType(builtinType()),
+ anyOf(hasDescendant(declRefExpr()),
+ declRefExpr())))))
+ .bind("node")))))
+ .bind("call"),
+ &ArithmeticArg);
+ AstMatcher.addMatcher(
+ cxxConstructExpr(
+ allOf(hasDeclaration(noArithmeticExprInArgs()),
+ anyOf(hasDescendant(
+ binaryOperator(
+ allOf(binaryArithmeticOperator(),
+ hasLHS(hasDescendant(declRefExpr())),
+ hasRHS(hasDescendant(declRefExpr()))))
+ .bind("node")),
+ hasDescendant(
+ unaryOperator(
+ allOf(unaryArithmeticOperator(),
+ hasUnaryOperand(allOf(
+ hasType(builtinType()),
+ anyOf(hasDescendant(declRefExpr()),
+ declRefExpr())))))
+ .bind("node")))))
+ .bind("call"),
+ &ArithmeticArg);
+
+ AstMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"),
+ &TrivialCtorDtor);
+
+ 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"),
+ &NaNExpr);
+
+ // First, look for direct parents of the MemberExpr.
+ AstMatcher.addMatcher(
+ callExpr(
+ callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
+ hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
+ .bind("member")))
+ .bind("node"),
+ &NoAddRefReleaseOnReturn);
+ // 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(
+ callExpr(
+ callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
+ hasParent(castExpr(
+ hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
+ .bind("member")))))
+ .bind("node"),
+ &NoAddRefReleaseOnReturn);
+
+ // 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(
+ functionDecl(returns(recordType(hasDeclaration(cxxRecordDecl(
+ isLambdaDecl()).bind("decl"))))),
+ &RefCountedInsideLambda);
+ AstMatcher.addMatcher(lambdaExpr().bind("lambdaExpr"),
+ &RefCountedInsideLambda);
+ AstMatcher.addMatcher(
+ classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
+ recordType(hasDeclaration(cxxRecordDecl(
+ isLambdaDecl()).bind("decl")))))),
+ &RefCountedInsideLambda);
+
+ // 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(
+ cxxMethodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool")))
+ .bind("node"),
+ &ExplicitOperatorBool);
+
+ AstMatcher.addMatcher(cxxRecordDecl().bind("decl"), &NoDuplicateRefCntMember);
+
+ AstMatcher.addMatcher(
+ classTemplateSpecializationDecl(
+ allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
+ hasNeedsNoVTableTypeAttr()))
+ .bind("node"),
+ &NeedsNoVTableType);
+
+ // Handle non-mem-movable template specializations
+ AstMatcher.addMatcher(
+ classTemplateSpecializationDecl(
+ allOf(needsMemMovableTemplateArg(),
+ hasAnyTemplateArgument(refersToType(isNonMemMovable()))))
+ .bind("specialization"),
+ &NonMemMovableTemplateArg);
+
+ // Handle non-mem-movable members
+ AstMatcher.addMatcher(
+ cxxRecordDecl(needsMemMovableMembers())
+ .bind("decl"),
+ &NonMemMovableMember);
+
+ AstMatcher.addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(),
+ ofClass(allOf(isConcreteClass(),
+ decl().bind("class"))),
+ unless(isMarkedImplicit()))
+ .bind("ctor"),
+ &ExplicitImplicit);
+
+ AstMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"),
+ &NoAutoType);
+
+ AstMatcher.addMatcher(
+ cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"),
+ &NoExplicitMoveConstructor);
+
+ AstMatcher.addMatcher(
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
+ ofClass(hasRefCntMember()))))
+ .bind("node"),
+ &RefCountedCopyConstructor);
+
+ AstMatcher.addMatcher(
+ callExpr(isAssertAssignmentTestFunc()).bind("funcCall"),
+ &AssertAttribution);
+
+ AstMatcher.addMatcher(varDecl(hasType(isRefPtr())).bind("decl"),
+ &KungFuDeathGrip);
+
+ 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"),
+ &SprintfLiteral
+ );
+
+ AstMatcher.addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"),
+ &OverrideBaseCall);
+
+ AstMatcher.addMatcher(
+ cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"),
+ &OverrideBaseCallUsage);
+
+ AstMatcher.addMatcher(
+ functionDecl(anyOf(allOf(isDefinition(),
+ hasAncestor(classTemplateSpecializationDecl()
+ .bind("spec"))),
+ isDefinition()))
+ .bind("func"),
+ &NonParamInsideFunctionDecl);
+ AstMatcher.addMatcher(
+ lambdaExpr().bind("lambda"),
+ &NonParamInsideFunctionDecl);
+}
+
+// These enum variants determine whether an allocation has occured in the code.
+enum AllocationVariety {
+ AV_None,
+ AV_Global,
+ AV_Automatic,
+ AV_Temporary,
+ AV_Heap,
+};
+
+// 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 DiagnosticsMatcher::ScopeChecker::run(
+ 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()) {
+ return;
+ }
+ if (const Expr *Default = D->getDefaultArg()) {
+ if (const MaterializeTemporaryExpr *E =
+ dyn_cast<MaterializeTemporaryExpr>(Default)) {
+ // We have just found a ParmVarDecl which has, as its default argument,
+ // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as
+ // automatic, by adding it to the AutomaticTemporaryMap.
+ // Reporting on this type will occur when the MaterializeTemporaryExpr
+ // is matched against.
+ AutomaticTemporaries[E] = D;
+ }
+ }
+ return;
+ }
+
+ // Determine the type of allocation which we detected
+ if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) {
+ if (D->hasGlobalStorage()) {
+ Variety = AV_Global;
+ } else {
+ Variety = AV_Automatic;
+ }
+ T = D->getType();
+ Loc = D->getLocStart();
+ } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
+ // New allocates things on the heap.
+ // We don't consider placement new to do anything, as it doesn't actually
+ // allocate the storage, and thus gives us no useful information.
+ if (!isPlacementNew(E)) {
+ Variety = AV_Heap;
+ T = E->getAllocatedType();
+ Loc = E->getLocStart();
+ }
+ } else if (const MaterializeTemporaryExpr *E =
+ Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) {
+ // Temporaries can actually have varying storage durations, due to temporary
+ // lifetime extension. We consider the allocation variety of this temporary
+ // to be the same as the allocation variety of its lifetime.
+
+ // XXX We maybe should mark these lifetimes as being due to a temporary
+ // which has had its lifetime extended, to improve the error messages.
+ switch (E->getStorageDuration()) {
+ case SD_FullExpression: {
+ // Check if this temporary is allocated as a default argument!
+ // if it is, we want to pretend that it is automatic.
+ AutomaticTemporaryMap::iterator AutomaticTemporary =
+ AutomaticTemporaries.find(E);
+ if (AutomaticTemporary != AutomaticTemporaries.end()) {
+ Variety = AV_Automatic;
+ } else {
+ Variety = AV_Temporary;
+ }
+ } break;
+ case SD_Automatic:
+ Variety = AV_Automatic;
+ break;
+ case SD_Thread:
+ case SD_Static:
+ Variety = AV_Global;
+ break;
+ case SD_Dynamic:
+ assert(false && "I don't think that this ever should occur...");
+ Variety = AV_Heap;
+ break;
+ }
+ T = E->getType().getUnqualifiedType();
+ Loc = E->getLocStart();
+ } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) {
+ T = E->getType()->getPointeeType();
+ if (!T.isNull()) {
+ // 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");
+
+ 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");
+
+ // 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);
+ break;
+
+ case AV_Automatic:
+ GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, StackNoteID);
+ HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, StackNoteID);
+ 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);
+ 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);
+ break;
+ }
+}
+
+void DiagnosticsMatcher::ArithmeticArgChecker::run(
+ 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 Expr *Expression = Result.Nodes.getNodeAs<Expr>("node");
+ if (const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
+ Diag.Report(Expression->getLocStart(), ErrorID) << Call->getDirectCallee();
+ } else if (const CXXConstructExpr *Ctr =
+ Result.Nodes.getNodeAs<CXXConstructExpr>("call")) {
+ Diag.Report(Expression->getLocStart(), ErrorID) << Ctr->getConstructor();
+ }
+}
+
+void DiagnosticsMatcher::TrivialCtorDtorChecker::run(
+ 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 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;
+}
+
+void DiagnosticsMatcher::NaNExprChecker::run(
+ 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());
+ // The AST subtree that we are looking for will look like this:
+ // -BinaryOperator ==/!=
+ // |-ImplicitCastExpr LValueToRValue
+ // | |-DeclRefExpr
+ // |-ImplicitCastExpr LValueToRValue
+ // |-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);
+ }
+}
+
+void DiagnosticsMatcher::NoAddRefReleaseOnReturnChecker::run(
+ 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;
+}
+
+void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run(
+ const MatchFinder::MatchResult &Result) {
+ Context = Result.Context;
+ static DenseSet<const CXXRecordDecl*> CheckedDecls;
+
+ 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()) {
+ Lambda = Record;
+ }
+ }
+
+ if (!Lambda || !Lambda->isLambda()) {
+ return;
+ }
+
+ // Don't report errors on the same declarations more than once.
+ if (CheckedDecls.count(Lambda)) {
+ return;
+ }
+ CheckedDecls.insert(Lambda);
+
+ bool StrongRefToThisCaptured = false;
+
+ for (const LambdaCapture& Capture : Lambda->captures()) {
+ // Check if any of the captures are ByRef. If they are, we have nothing to
+ // report, as it's OK to capture raw pointers to refcounted objects so long as
+ // the Lambda doesn't escape the current scope, which is required by ByRef
+ // captures already.
+ if (Capture.getCaptureKind() == LCK_ByRef) {
+ return;
+ }
+
+ // Check if this capture is byvalue, and captures a strong reference to this.
+ // XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow?
+ if (!StrongRefToThisCaptured &&
+ Capture.capturesVariable() &&
+ Capture.getCaptureKind() == LCK_ByCopy) {
+ const VarDecl *Var = Capture.getCapturedVar();
+ if (Var->hasInit()) {
+ const Stmt *Init = Var->getInit();
+
+ // Ignore single argument constructors, and trivial nodes.
+ while (true) {
+ auto NewInit = IgnoreImplicit(Init);
+ if (auto ConstructExpr = dyn_cast<CXXConstructExpr>(NewInit)) {
+ if (ConstructExpr->getNumArgs() == 1) {
+ NewInit = ConstructExpr->getArg(0);
+ }
+ }
+ if (Init == NewInit) {
+ break;
+ }
+ Init = NewInit;
+ }
+
+ if (isa<CXXThisExpr>(Init)) {
+ StrongRefToThisCaptured = true;
+ }
+ }
+ }
+ }
+
+ // Now we can go through and produce errors for any captured variables or this pointers.
+ for (const LambdaCapture& Capture : Lambda->captures()) {
+ if (Capture.capturesVariable()) {
+ QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
+
+ if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
+ emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee);
+ return;
+ }
+ }
+
+ // The situation with captures of `this` is more complex. All captures of
+ // `this` look the same-ish (they are LCK_This). We want to complain about
+ // captures of `this` where `this` is a refcounted type, and the capture is
+ // actually used in the body of the lambda (if the capture isn't used, then
+ // we don't care, because it's only being captured in order to give access
+ // to private methods).
+ //
+ // In addition, we don't complain about this, even if it is used, if it was
+ // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that
+ // expresses the intent that the lambda won't leave the enclosing scope.
+ bool ImplicitByRefDefaultedCapture =
+ Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
+ if (Capture.capturesThis() &&
+ !ImplicitByRefDefaultedCapture &&
+ !StrongRefToThisCaptured) {
+ ThisVisitor V(*this);
+ bool NotAborted = V.TraverseDecl(const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
+ if (!NotAborted) {
+ return;
+ }
+ }
+ }
+}
+
+void DiagnosticsMatcher::RefCountedInsideLambdaChecker::emitDiagnostics(
+ SourceLocation Loc, StringRef Name, QualType Type) {
+ DiagnosticsEngine& Diag = 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");
+
+ Diag.Report(Loc, ErrorID) << Name << Type;
+ Diag.Report(Loc, NoteID);
+}
+
+bool DiagnosticsMatcher::RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) {
+ QualType Pointee = This->getType()->getPointeeType();
+ if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
+ Checker.emitDiagnostics(This->getLocStart(), "this", Pointee);
+ return false;
+ }
+
+ return true;
+}
+
+void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run(
+ 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'";
+ }
+}
+
+void DiagnosticsMatcher::NoDuplicateRefCntMemberChecker::run(
+ 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();
+
+ // If we don't have an mRefCnt member, and we have less than 2 superclasses,
+ // we don't have to run this loop, as neither case will ever apply.
+ if (!RefCntMember && D->getNumBases() < 2) {
+ return;
+ }
+
+ // Check every superclass for whether it has a base with a refcnt member, and
+ // 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");
+
+ Diag.Report(D->getLocStart(), Error) << D;
+ Diag.Report(BaseRefCntMember->getLocStart(), Note1)
+ << BaseRefCntMember->getParent();
+ Diag.Report(RefCntMember->getLocStart(), Note2);
+ }
+
+ 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");
+
+ // superclass has mRefCnt, and another superclass also has an mRefCnt
+ Diag.Report(D->getLocStart(), Error) << D;
+ Diag.Report(BaseRefCntMember->getLocStart(), Note)
+ << BaseRefCntMember->getParent();
+ Diag.Report(FoundRefCntBase->getLocStart(), Note)
+ << FoundRefCntBase->getParent();
+ }
+
+ // Record that we've found a base with a mRefCnt member
+ FoundRefCntBase = BaseRefCntMember;
+ }
+ }
+}
+
+void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
+ 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;
+}
+
+void DiagnosticsMatcher::NonMemMovableTemplateArgChecker::run(
+ 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");
+
+ // 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;
+ // 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);
+ }
+ }
+}
+
+void DiagnosticsMatcher::NonMemMovableMemberChecker::run(
+ 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");
+
+ // 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());
+ }
+ }
+}
+
+void DiagnosticsMatcher::ExplicitImplicitChecker::run(
+ 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);
+}
+
+void DiagnosticsMatcher::NoAutoTypeChecker::run(
+ 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);
+}
+
+void DiagnosticsMatcher::NoExplicitMoveConstructorChecker::run(
+ 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);
+}
+
+void DiagnosticsMatcher::RefCountedCopyConstructorChecker::run(
+ 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,
+ "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");
+
+ // 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);
+}
+
+void DiagnosticsMatcher::AssertAssignmentChecker::run(
+ 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);
+ }
+}
+
+void DiagnosticsMatcher::KungFuDeathGripChecker::run(
+ 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 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)) {
+ return;
+ }
+
+ const Expr *E = IgnoreImplicit(D->getInit());
+ const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E);
+ if (CE && CE->getNumArgs() == 0) {
+ // We don't report an error when we construct and don't use a nsCOMPtr /
+ // nsRefPtr with no arguments. We don't report it because the error is not
+ // related to the current check. In the future it may be reported through a
+ // more generic mechanism.
+ return;
+ }
+
+ // We don't want to look at the single argument conversion constructors
+ // which are inbetween the declaration and the actual object which we are
+ // assigning into the nsCOMPtr/RefPtr. To do this, we repeatedly
+ // IgnoreImplicit, then look at the expression. If it is one of these
+ // conversion constructors, we ignore it and continue to dig.
+ while ((CE = dyn_cast<CXXConstructExpr>(E)) && CE->getNumArgs() == 1) {
+ E = IgnoreImplicit(CE->getArg(0));
+ }
+
+ // We allow taking a kungFuDeathGrip of `this` because it cannot change
+ // beneath us, so calling directly through `this` is OK. This is the same
+ // for local variable declarations.
+ //
+ // We also don't complain about unused RefPtrs which are constructed from
+ // the return value of a new expression, as these are required in order to
+ // immediately destroy the value created (which was presumably created for
+ // its side effects), and are not used as a death grip.
+ if (isa<CXXThisExpr>(E) || isa<DeclRefExpr>(E) || isa<CXXNewExpr>(E)) {
+ return;
+ }
+
+ // These types are assigned into nsCOMPtr and RefPtr for their side effects,
+ // and not as a kungFuDeathGrip. We don't want to consider RefPtr and nsCOMPtr
+ // types which are initialized with these types as errors.
+ const TagDecl *TD = E->getType()->getAsTagDecl();
+ if (TD && TD->getIdentifier()) {
+ static const char *IgnoreTypes[] = {
+ "already_AddRefed",
+ "nsGetServiceByCID",
+ "nsGetServiceByCIDWithError",
+ "nsGetServiceByContractID",
+ "nsGetServiceByContractIDWithError",
+ "nsCreateInstanceByCID",
+ "nsCreateInstanceByContractID",
+ "nsCreateInstanceFromFactory",
+ };
+
+ for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]); ++i) {
+ if (TD->getName() == IgnoreTypes[i]) {
+ return;
+ }
+ }
+ }
+
+ // Report the error
+ const char *ErrThing;
+ const char *NoteThing;
+ if (isa<MemberExpr>(E)) {
+ 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);
+}
+
+void DiagnosticsMatcher::SprintfLiteralChecker::run(
+ 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 CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall");
+
+ StringRef Name = D->getDirectCallee()->getName();
+ const char *Replacement;
+ if (Name == "snprintf") {
+ Replacement = "SprintfLiteral";
+ } else {
+ assert(Name == "vsnprintf");
+ Replacement = "VsprintfLiteral";
+ }
+
+ 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;
+ 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;
+ }
+ }
+}
+
+bool DiagnosticsMatcher::OverrideBaseCallChecker::isRequiredBaseMethod(
+ const CXXMethodDecl *Method) {
+ return MozChecker::hasCustomAnnotation(Method, "moz_required_base_method");
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::evaluateExpression(
+ const Stmt *StmtExpr, std::list<const CXXMethodDecl*> &MethodList) {
+ // Continue while we have methods in our list
+ if (!MethodList.size()) {
+ return;
+ }
+
+ if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) {
+ if (auto Method = dyn_cast<CXXMethodDecl>(
+ MemberFuncCall->getDirectCallee())) {
+ findBaseMethodCall(Method, MethodList);
+ }
+ }
+
+ for (auto S : StmtExpr->children()) {
+ if (S) {
+ evaluateExpression(S, MethodList);
+ }
+ }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::getRequiredBaseMethod(
+ const CXXMethodDecl *Method,
+ std::list<const CXXMethodDecl*>& MethodsList) {
+
+ if (isRequiredBaseMethod(Method)) {
+ MethodsList.push_back(Method);
+ } else {
+ // Loop through all it's base methods.
+ for (auto BaseMethod = Method->begin_overridden_methods();
+ BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+ getRequiredBaseMethod(*BaseMethod, MethodsList);
+ }
+ }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::findBaseMethodCall(
+ const CXXMethodDecl* Method,
+ std::list<const CXXMethodDecl*>& MethodsList) {
+
+ 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 DiagnosticsMatcher::OverrideBaseCallChecker::run(
+ const MatchFinder::MatchResult &Result) {
+ DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+ unsigned OverrideBaseCallCheckID = Diag.getDiagnosticIDs()->getCustomDiagID(
+ DiagnosticIDs::Error,
+ "Method %0 must be called in all overrides, but is not called in "
+ "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;
+ }
+
+ // Preferred the usage of list instead of vector in order to avoid
+ // calling erase-remove when deleting items
+ std::list<const CXXMethodDecl*> MethodsList;
+ // For each overridden method push it to a list if it meets our
+ // criteria
+ for (auto BaseMethod = Method->begin_overridden_methods();
+ BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+ getRequiredBaseMethod(*BaseMethod, MethodsList);
+ }
+
+ // If no method has been found then no annotation was used
+ // so checking is not needed
+ if (!MethodsList.size()) {
+ continue;
+ }
+
+ // 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)
+ << BaseMethod->getQualifiedNameAsString()
+ << Decl->getName();
+ }
+ }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallUsageChecker::run(
+ 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 CXXMethodDecl *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+
+ Diag.Report(Method->getLocation(), ErrorID);
+}
+
+void DiagnosticsMatcher::NonParamInsideFunctionDeclChecker::run(
+ 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();
+ }
+ }
+
+ if (!func) {
+ return;
+ }
+
+ if (func->isDeleted()) {
+ return;
+ }
+
+ // Don't report errors on the same declarations more than once.
+ 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);
+
+ if (Spec) {
+ Diag.Report(Spec->getPointOfInstantiation(), SpecNoteID)
+ << Spec->getSpecializedTemplate();
+ }
+ }
+ }
+}
+
+class MozCheckAction : public PluginASTAction {
+public:
+ ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
+ StringRef FileName) override {
+#if CLANG_VERSION_FULL >= 306
+ std::unique_ptr<MozChecker> Checker(llvm::make_unique<MozChecker>(CI));
+ ASTConsumerPtr Other(Checker->getOtherConsumer());
+
+ std::vector<ASTConsumerPtr> Consumers;
+ Consumers.push_back(std::move(Checker));
+ Consumers.push_back(std::move(Other));
+ return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
+#else
+ MozChecker *Checker = new MozChecker(CI);
+
+ ASTConsumer *Consumers[] = {Checker, Checker->getOtherConsumer()};
+ return new MultiplexConsumer(Consumers);
+#endif
+ }
+
+ bool ParseArgs(const CompilerInstance &CI,
+ const std::vector<std::string> &Args) override {
+ return true;
+ }
+};
+}
+
+static FrontendPluginRegistry::Add<MozCheckAction> X("moz-check",
+ "check moz action");
+// Export the registry on Windows.
+#ifdef LLVM_EXPORT_REGISTRY
+LLVM_EXPORT_REGISTRY(FrontendPluginRegistry)
+#endif