forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1514 lines
		
	
	
	
		
			56 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1514 lines
		
	
	
	
		
			56 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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 "clang/AST/ASTConsumer.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/AST/RecursiveASTVisitor.h"
 | |
| #include "clang/ASTMatchers/ASTMatchers.h"
 | |
| #include "clang/ASTMatchers/ASTMatchFinder.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>
 | |
| 
 | |
| #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
 | |
| 
 | |
| #if CLANG_VERSION_FULL < 308
 | |
| // 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
 | |
| 
 | |
| 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);
 | |
|   };
 | |
| 
 | |
|   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 NonMemMovableChecker : 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);
 | |
|   };
 | |
| 
 | |
|   ScopeChecker scopeChecker;
 | |
|   ArithmeticArgChecker arithmeticArgChecker;
 | |
|   TrivialCtorDtorChecker trivialCtorDtorChecker;
 | |
|   NaNExprChecker nanExprChecker;
 | |
|   NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
 | |
|   RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
 | |
|   ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
 | |
|   NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
 | |
|   NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
 | |
|   NonMemMovableChecker nonMemMovableChecker;
 | |
|   ExplicitImplicitChecker explicitImplicitChecker;
 | |
|   NoAutoTypeChecker noAutoTypeChecker;
 | |
|   NoExplicitMoveConstructorChecker noExplicitMoveConstructorChecker;
 | |
|   RefCountedCopyConstructorChecker refCountedCopyConstructorChecker;
 | |
|   MatchFinder astMatcher;
 | |
| };
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| std::string getDeclarationNamespace(const Decl *decl) {
 | |
|   const DeclContext *DC =
 | |
|       decl->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 *decl) {
 | |
|   std::string name = getDeclarationNamespace(decl);
 | |
|   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
 | |
| }
 | |
| 
 | |
| bool isInIgnoredNamespaceForImplicitConversion(const Decl *decl) {
 | |
|   std::string name = getDeclarationNamespace(decl);
 | |
|   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 *decl) {
 | |
|   SourceLocation Loc = decl->getLocation();
 | |
|   const SourceManager &SM = decl->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) {
 | |
|       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 *decl) {
 | |
|   decl = decl->getCanonicalDecl();
 | |
|   SourceLocation Loc = decl->getLocation();
 | |
|   const SourceManager &SM = decl->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;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool isInterestingDeclForImplicitConversion(const Decl *decl) {
 | |
|   return !isInIgnoredNamespaceForImplicitConversion(decl) &&
 | |
|          !isIgnoredPathForImplicitConversion(decl);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| }
 | |
| 
 | |
| 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);
 | |
| 
 | |
| 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", "must-use");
 | |
| 
 | |
| 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 = D->getName();
 | |
|       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) {
 | |
|     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 *stmt) {
 | |
|     const Expr *E = dyn_cast_or_null<Expr>(stmt);
 | |
|     if (E) {
 | |
|       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 must_overrides;
 | |
|     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"))
 | |
|           must_overrides.push_back(*M);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     for (OverridesVector::iterator it = must_overrides.begin();
 | |
|          it != must_overrides.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 (M->getName() == (*it)->getName() &&
 | |
|             !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 *stmt) {
 | |
|     HandleUnusedExprResult(stmt->getSubStmt());
 | |
|     return true;
 | |
|   }
 | |
|   bool VisitCompoundStmt(CompoundStmt *stmt) {
 | |
|     for (CompoundStmt::body_iterator it = stmt->body_begin(),
 | |
|                                      e = stmt->body_end();
 | |
|          it != e; ++it) {
 | |
|       HandleUnusedExprResult(*it);
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   bool VisitIfStmt(IfStmt *Stmt) {
 | |
|     HandleUnusedExprResult(Stmt->getThen());
 | |
|     HandleUnusedExprResult(Stmt->getElse());
 | |
|     return true;
 | |
|   }
 | |
|   bool VisitWhileStmt(WhileStmt *Stmt) {
 | |
|     HandleUnusedExprResult(Stmt->getBody());
 | |
|     return true;
 | |
|   }
 | |
|   bool VisitDoStmt(DoStmt *Stmt) {
 | |
|     HandleUnusedExprResult(Stmt->getBody());
 | |
|     return true;
 | |
|   }
 | |
|   bool VisitForStmt(ForStmt *Stmt) {
 | |
|     HandleUnusedExprResult(Stmt->getBody());
 | |
|     HandleUnusedExprResult(Stmt->getInit());
 | |
|     HandleUnusedExprResult(Stmt->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 = method->getName();
 | |
|     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 ArrayType *arrTy = T->getAsArrayTypeUnsafe())
 | |
|     T = arrTy->getElementType();
 | |
|   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
 | |
|   return clazz ? isClassRefCounted(clazz) : false;
 | |
| }
 | |
| 
 | |
| template <class T> bool IsInSystemHeader(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 (field->getName() == "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 ArrayType *arrTy = T->getAsArrayTypeUnsafe())
 | |
|     T = arrTy->getElementType();
 | |
|   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
 | |
|   return clazz ? getBaseRefCntMember(clazz) : 0;
 | |
| }
 | |
| 
 | |
| bool typeHasVTable(QualType T) {
 | |
|   while (const 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 IsInSystemHeader(Finder->getASTContext(), Node);
 | |
| }
 | |
| 
 | |
| /// This matcher will match locations in SkScalar.h.  This header contains a
 | |
| /// known NaN-testing expression which we would like to whitelist.
 | |
| AST_MATCHER(BinaryOperator, isInSkScalarDotH) {
 | |
|   SourceLocation Loc = Node.getOperatorLoc();
 | |
|   auto &SourceManager = Finder->getASTContext().getSourceManager();
 | |
|   SmallString<1024> FileName = SourceManager.getFilename(Loc);
 | |
|   return llvm::sys::path::rbegin(FileName)->equals("SkScalar.h");
 | |
| }
 | |
| 
 | |
| /// 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 = Method->getName();
 | |
|     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, needsMemMovable) {
 | |
|   return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
 | |
| }
 | |
| 
 | |
| AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
 | |
|   const CXXConstructorDecl *decl = Node.getCanonicalDecl();
 | |
|   return
 | |
|       // Skip ignored namespaces and paths
 | |
|       !isInIgnoredNamespaceForImplicitCtor(decl) &&
 | |
|       !isIgnoredPathForImplicitCtor(decl) &&
 | |
|       // We only want Converting constructors
 | |
|       decl->isConvertingConstructor(false) &&
 | |
|       // We don't want copy of move constructors, as those are allowed to be
 | |
|       // implicit
 | |
|       !decl->isCopyOrMoveConstructor() &&
 | |
|       // We don't want deleted constructors.
 | |
|       !decl->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();
 | |
| }
 | |
| }
 | |
| }
 | |
| 
 | |
| 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 *Decl = T->getAsCXXRecordDecl();
 | |
|       assert(Decl && "This type should be a C++ class");
 | |
| 
 | |
|       Diag.Report(Decl->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 *Decl = T->getAsCXXRecordDecl();
 | |
|       assert(Decl && "This type should be a C++ class");
 | |
| 
 | |
|       Diag.Report(Decl->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 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 *Decl = T->getAsCXXRecordDecl()) {
 | |
|     if (Decl->hasDefinition()) {
 | |
|       Decl = Decl->getDefinition();
 | |
| 
 | |
|       for (const CXXBaseSpecifier &Base : Decl->bases()) {
 | |
|         if (hasEffectiveAnnotation(Base.getType())) {
 | |
|           AnnotationReason Reason = {Base.getType(), RK_BaseClass, nullptr};
 | |
|           Cache[Key] = Reason;
 | |
|           return Reason;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Recurse into members
 | |
|       for (const FieldDecl *Field : Decl->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(
 | |
|               Decl, "moz_inherit_type_annotations_from_template_args")) {
 | |
|         const ClassTemplateSpecializationDecl *Spec =
 | |
|             dyn_cast<ClassTemplateSpecializationDecl>(Decl);
 | |
|         if (Spec) {
 | |
|           const TemplateArgumentList &Args = Spec->getTemplateArgs();
 | |
| 
 | |
|           for (const TemplateArgument &Arg : Args.asArray()) {
 | |
|             if (Arg.getKind() == TemplateArgument::Type) {
 | |
|               QualType Type = Arg.getAsType();
 | |
| 
 | |
|               if (hasEffectiveAnnotation(Type)) {
 | |
|                 AnnotationReason Reason = {Type, RK_TemplateInherited, nullptr};
 | |
|                 Cache[Key] = Reason;
 | |
|                 return Reason;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AnnotationReason Reason = {QualType(), RK_None, nullptr};
 | |
|   Cache[Key] = Reason;
 | |
|   return Reason;
 | |
| }
 | |
| 
 | |
| bool isPlacementNew(const CXXNewExpr *Expr) {
 | |
|   // Regular new expressions aren't placement new
 | |
|   if (Expr->getNumPlacementArgs() == 0)
 | |
|     return false;
 | |
|   const FunctionDecl *Decl = Expr->getOperatorNew();
 | |
|   if (Decl && MozChecker::hasCustomAnnotation(Decl, "moz_heap_allocator")) {
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| DiagnosticsMatcher::DiagnosticsMatcher() {
 | |
|   astMatcher.addMatcher(varDecl().bind("node"), &scopeChecker);
 | |
|   astMatcher.addMatcher(cxxNewExpr().bind("node"), &scopeChecker);
 | |
|   astMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &scopeChecker);
 | |
|   astMatcher.addMatcher(
 | |
|       callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
 | |
|       &scopeChecker);
 | |
|   astMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &scopeChecker);
 | |
| 
 | |
|   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"),
 | |
|       &arithmeticArgChecker);
 | |
|   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"),
 | |
|       &arithmeticArgChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"),
 | |
|                         &trivialCtorDtorChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(
 | |
|       binaryOperator(
 | |
|           allOf(binaryEqualityOperator(),
 | |
|                 hasLHS(has(
 | |
|                     declRefExpr(hasType(qualType((isFloat())))).bind("lhs"))),
 | |
|                 hasRHS(has(
 | |
|                     declRefExpr(hasType(qualType((isFloat())))).bind("rhs"))),
 | |
|                 unless(anyOf(isInSystemHeader(), isInSkScalarDotH()))))
 | |
|           .bind("node"),
 | |
|       &nanExprChecker);
 | |
| 
 | |
|   // 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"),
 | |
|       &noAddRefReleaseOnReturnChecker);
 | |
|   // 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"),
 | |
|       &noAddRefReleaseOnReturnChecker);
 | |
| 
 | |
|   // Match declrefs with type "pointer to object of ref-counted type" inside a
 | |
|   // lambda, where the declaration they reference is not inside the lambda.
 | |
|   // This excludes arguments and local variables, leaving only captured
 | |
|   // variables.
 | |
|   astMatcher.addMatcher(lambdaExpr().bind("lambda"), &refCountedInsideLambdaChecker);
 | |
| 
 | |
|   // 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"),
 | |
|       &explicitOperatorBoolChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(
 | |
|       cxxRecordDecl(allOf(decl().bind("decl"), hasRefCntMember())),
 | |
|       &noDuplicateRefCntMemberChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(
 | |
|       classTemplateSpecializationDecl(
 | |
|           allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
 | |
|                 hasNeedsNoVTableTypeAttr()))
 | |
|           .bind("node"),
 | |
|       &needsNoVTableTypeChecker);
 | |
| 
 | |
|   // Handle non-mem-movable template specializations
 | |
|   astMatcher.addMatcher(
 | |
|       classTemplateSpecializationDecl(
 | |
|           allOf(needsMemMovable(),
 | |
|                 hasAnyTemplateArgument(refersToType(isNonMemMovable()))))
 | |
|           .bind("specialization"),
 | |
|       &nonMemMovableChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(
 | |
|       cxxConstructorDecl(isInterestingImplicitCtor(),
 | |
|                          ofClass(allOf(isConcreteClass(), decl().bind("class"))),
 | |
|                          unless(isMarkedImplicit()))
 | |
|           .bind("ctor"),
 | |
|       &explicitImplicitChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"),
 | |
|                         &noAutoTypeChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"),
 | |
|                         &noExplicitMoveConstructorChecker);
 | |
| 
 | |
|   astMatcher.addMatcher(cxxConstructExpr(hasDeclaration(
 | |
|                                           cxxConstructorDecl(
 | |
|                                               isCompilerProvidedCopyConstructor(),
 | |
|                                               ofClass(hasRefCntMember())))).bind("node"),
 | |
|                         &refCountedCopyConstructorChecker);
 | |
| }
 | |
| 
 | |
| // 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 (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 *expr = Result.Nodes.getNodeAs<Expr>("node");
 | |
|   if (const CallExpr *call = Result.Nodes.getNodeAs<CallExpr>("call")) {
 | |
|     Diag.Report(expr->getLocStart(), errorID) << call->getDirectCallee();
 | |
|   } else if (const CXXConstructExpr *ctr =
 | |
|                  Result.Nodes.getNodeAs<CXXConstructExpr>("call")) {
 | |
|     Diag.Report(expr->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 *expr = 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>(expr->getLHS());
 | |
|   const ImplicitCastExpr *rhsExpr = dyn_cast<ImplicitCastExpr>(expr->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(expr->getLocStart(), errorID);
 | |
|     Diag.Report(expr->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) {
 | |
|   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 LambdaExpr *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("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);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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") &&
 | |
|       !IsInSystemHeader(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();
 | |
|   unsigned warningID = Diag.getDiagnosticIDs()->getCustomDiagID(
 | |
|       DiagnosticIDs::Error,
 | |
|       "Refcounted record %0 has multiple mRefCnt members");
 | |
|   unsigned note1ID = Diag.getDiagnosticIDs()->getCustomDiagID(
 | |
|       DiagnosticIDs::Note, "Superclass %0 also has an mRefCnt member");
 | |
|   unsigned note2ID = Diag.getDiagnosticIDs()->getCustomDiagID(
 | |
|       DiagnosticIDs::Note,
 | |
|       "Consider using the _INHERITED macros for AddRef and Release here");
 | |
| 
 | |
|   const CXXRecordDecl *decl = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
 | |
|   const FieldDecl *refCntMember = getClassRefCntMember(decl);
 | |
|   assert(refCntMember &&
 | |
|          "The matcher checked to make sure we have a refCntMember");
 | |
| 
 | |
|   // Check every superclass for whether it has a base with a refcnt member, and
 | |
|   // warn for those which do
 | |
|   for (CXXRecordDecl::base_class_const_iterator base = decl->bases_begin(),
 | |
|                                                 e = decl->bases_end();
 | |
|        base != e; ++base) {
 | |
|     const FieldDecl *baseRefCntMember = getBaseRefCntMember(base->getType());
 | |
|     if (baseRefCntMember) {
 | |
|       Diag.Report(decl->getLocStart(), warningID) << decl;
 | |
|       Diag.Report(baseRefCntMember->getLocStart(), note1ID)
 | |
|           << baseRefCntMember->getParent();
 | |
|       Diag.Report(refCntMember->getLocStart(), note2ID);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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::NonMemMovableChecker::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(args[i].getAsType())) {
 | |
|       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::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 *Decl = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
 | |
| 
 | |
|   Diag.Report(Ctor->getLocation(), ErrorID) << Decl->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);
 | |
| }
 | |
| 
 | |
| 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");
 | 
