build/clang-plugin/clang-plugin.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 #include "clang/AST/ASTConsumer.h"
     5 #include "clang/AST/ASTContext.h"
     6 #include "clang/AST/RecursiveASTVisitor.h"
     7 #include "clang/ASTMatchers/ASTMatchers.h"
     8 #include "clang/ASTMatchers/ASTMatchFinder.h"
     9 #include "clang/Basic/Version.h"
    10 #include "clang/Frontend/CompilerInstance.h"
    11 #include "clang/Frontend/FrontendPluginRegistry.h"
    12 #include "clang/Frontend/MultiplexConsumer.h"
    13 #include "clang/Sema/Sema.h"
    14 #include "llvm/ADT/DenseMap.h"
    16 #define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
    18 using namespace llvm;
    19 using namespace clang;
    21 namespace {
    23 using namespace clang::ast_matchers;
    24 class DiagnosticsMatcher {
    25 public:
    26   DiagnosticsMatcher();
    28   ASTConsumer *makeASTConsumer() {
    29     return astMatcher.newASTConsumer();
    30   }
    32 private:
    33   class StackClassChecker : public MatchFinder::MatchCallback {
    34   public:
    35     virtual void run(const MatchFinder::MatchResult &Result);
    36     void noteInferred(QualType T, DiagnosticsEngine &Diag);
    37   };
    39   class NonHeapClassChecker : public MatchFinder::MatchCallback {
    40   public:
    41     virtual void run(const MatchFinder::MatchResult &Result);
    42     void noteInferred(QualType T, DiagnosticsEngine &Diag);
    43   };
    45   StackClassChecker stackClassChecker;
    46   NonHeapClassChecker nonheapClassChecker;
    47   MatchFinder astMatcher;
    48 };
    50 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
    51   DiagnosticsEngine &Diag;
    52   const CompilerInstance &CI;
    53   DiagnosticsMatcher matcher;
    54 public:
    55   MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {}
    57   ASTConsumer *getOtherConsumer() {
    58     return matcher.makeASTConsumer();
    59   }
    61   virtual void HandleTranslationUnit(ASTContext &ctx) {
    62     TraverseDecl(ctx.getTranslationUnitDecl());
    63   }
    65   static bool hasCustomAnnotation(const Decl *d, const char *spelling) {
    66     AnnotateAttr *attr = d->getAttr<AnnotateAttr>();
    67     if (!attr)
    68       return false;
    70     return attr->getAnnotation() == spelling;
    71   }
    73   bool VisitCXXRecordDecl(CXXRecordDecl *d) {
    74     // We need definitions, not declarations
    75     if (!d->isThisDeclarationADefinition()) return true;
    77     // Look through all of our immediate bases to find methods that need to be
    78     // overridden
    79     typedef std::vector<CXXMethodDecl *> OverridesVector;
    80     OverridesVector must_overrides;
    81     for (CXXRecordDecl::base_class_iterator base = d->bases_begin(),
    82          e = d->bases_end(); base != e; ++base) {
    83       // The base is either a class (CXXRecordDecl) or it's a templated class...
    84       CXXRecordDecl *parent = base->getType()
    85         .getDesugaredType(d->getASTContext())->getAsCXXRecordDecl();
    86       // The parent might not be resolved to a type yet. In this case, we can't
    87       // do any checking here. For complete correctness, we should visit
    88       // template instantiations, but this case is likely to be rare, so we will
    89       // ignore it until it becomes important.
    90       if (!parent) {
    91         continue;
    92       }
    93       parent = parent->getDefinition();
    94       for (CXXRecordDecl::method_iterator M = parent->method_begin();
    95           M != parent->method_end(); ++M) {
    96         if (hasCustomAnnotation(*M, "moz_must_override"))
    97           must_overrides.push_back(*M);
    98       }
    99     }
   101     for (OverridesVector::iterator it = must_overrides.begin();
   102         it != must_overrides.end(); ++it) {
   103       bool overridden = false;
   104       for (CXXRecordDecl::method_iterator M = d->method_begin();
   105           !overridden && M != d->method_end(); ++M) {
   106         // The way that Clang checks if a method M overrides its parent method
   107         // is if the method has the same name but would not overload.
   108         if (M->getName() == (*it)->getName() &&
   109             !CI.getSema().IsOverload(*M, (*it), false))
   110           overridden = true;
   111       }
   112       if (!overridden) {
   113         unsigned overrideID = Diag.getDiagnosticIDs()->getCustomDiagID(
   114             DiagnosticIDs::Error, "%0 must override %1");
   115         unsigned overrideNote = Diag.getDiagnosticIDs()->getCustomDiagID(
   116             DiagnosticIDs::Note, "function to override is here");
   117         Diag.Report(d->getLocation(), overrideID) << d->getDeclName() <<
   118           (*it)->getDeclName();
   119         Diag.Report((*it)->getLocation(), overrideNote);
   120       }
   121     }
   122     return true;
   123   }
   124 };
   126 /**
   127  * Where classes may be allocated. Regular classes can be allocated anywhere,
   128  * non-heap classes on the stack or as static variables, and stack classes only
   129  * on the stack. Note that stack classes subsumes non-heap classes.
   130  */
   131 enum ClassAllocationNature {
   132   RegularClass = 0,
   133   NonHeapClass = 1,
   134   StackClass = 2
   135 };
   137 /// A cached data of whether classes are stack classes, non-heap classes, or
   138 /// neither.
   139 DenseMap<const CXXRecordDecl *,
   140   std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses;
   142 ClassAllocationNature getClassAttrs(QualType T);
   144 ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
   145   // Normalize so that D points to the definition if it exists. If it doesn't,
   146   // then we can't allocate it anyways.
   147   if (!D->hasDefinition())
   148     return RegularClass;
   149   D = D->getDefinition();
   150   // Base class: anyone with this annotation is obviously a stack class
   151   if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
   152     return StackClass;
   154   // See if we cached the result.
   155   DenseMap<const CXXRecordDecl *,
   156     std::pair<const Decl *, ClassAllocationNature> >::iterator it =
   157     inferredAllocCauses.find(D);
   158   if (it != inferredAllocCauses.end()) {
   159     return it->second.second;
   160   }
   162   // Continue looking, we might be a stack class yet. Even if we're a nonheap
   163   // class, it might be possible that we've inferred to be a stack class.
   164   ClassAllocationNature type = RegularClass;
   165   if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) {
   166     type = NonHeapClass;
   167   }
   168   inferredAllocCauses.insert(std::make_pair(D,
   169     std::make_pair((const Decl *)0, type)));
   171   // Look through all base cases to figure out if the parent is a stack class or
   172   // a non-heap class. Since we might later infer to also be a stack class, keep
   173   // going.
   174   for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
   175        e = D->bases_end(); base != e; ++base) {
   176     ClassAllocationNature super = getClassAttrs(base->getType());
   177     if (super == StackClass) {
   178       inferredAllocCauses[D] = std::make_pair(
   179         base->getType()->getAsCXXRecordDecl(), StackClass);
   180       return StackClass;
   181     } else if (super == NonHeapClass) {
   182       inferredAllocCauses[D] = std::make_pair(
   183         base->getType()->getAsCXXRecordDecl(), NonHeapClass);
   184       type = NonHeapClass;
   185     }
   186   }
   188   // Maybe it has a member which is a stack class.
   189   for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
   190        field != e; ++field) {
   191     ClassAllocationNature fieldType = getClassAttrs(field->getType());
   192     if (fieldType == StackClass) {
   193       inferredAllocCauses[D] = std::make_pair(*field, StackClass);
   194       return StackClass;
   195     } else if (fieldType == NonHeapClass) {
   196       inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
   197       type = NonHeapClass;
   198     }
   199   }
   201   return type;
   202 }
   204 ClassAllocationNature getClassAttrs(QualType T) {
   205   while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
   206     T = arrTy->getElementType();
   207   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
   208   return clazz ? getClassAttrs(clazz) : RegularClass;
   209 }
   211 }
   213 namespace clang {
   214 namespace ast_matchers {
   216 /// This matcher will match any class with the stack class assertion or an
   217 /// array of such classes.
   218 AST_MATCHER(QualType, stackClassAggregate) {
   219   return getClassAttrs(Node) == StackClass;
   220 }
   222 /// This matcher will match any class with the stack class assertion or an
   223 /// array of such classes.
   224 AST_MATCHER(QualType, nonheapClassAggregate) {
   225   return getClassAttrs(Node) == NonHeapClass;
   226 }
   228 /// This matcher will match any function declaration that is declared as a heap
   229 /// allocator.
   230 AST_MATCHER(FunctionDecl, heapAllocator) {
   231   return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
   232 }
   233 }
   234 }
   236 namespace {
   238 bool isPlacementNew(const CXXNewExpr *expr) {
   239   // Regular new expressions aren't placement new
   240   if (expr->getNumPlacementArgs() == 0)
   241     return false;
   242   if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
   243       "moz_heap_allocator"))
   244     return false;
   245   return true;
   246 }
   248 DiagnosticsMatcher::DiagnosticsMatcher() {
   249   // Stack class assertion: non-local variables of a stack class are forbidden
   250   // (non-localness checked in the callback)
   251   astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
   252     &stackClassChecker);
   253   // Stack class assertion: new stack class is forbidden (unless placement new)
   254   astMatcher.addMatcher(newExpr(hasType(pointerType(
   255       pointee(stackClassAggregate())
   256     ))).bind("node"), &stackClassChecker);
   257   // Non-heap class assertion: new non-heap class is forbidden (unless placement
   258   // new)
   259   astMatcher.addMatcher(newExpr(hasType(pointerType(
   260       pointee(nonheapClassAggregate())
   261     ))).bind("node"), &nonheapClassChecker);
   263   // Any heap allocation function that returns a non-heap or a stack class is
   264   // definitely doing something wrong
   265   astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
   266       returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
   267     &nonheapClassChecker);
   268   astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
   269       returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
   270     &stackClassChecker);
   271 }
   273 void DiagnosticsMatcher::StackClassChecker::run(
   274     const MatchFinder::MatchResult &Result) {
   275   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   276   unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
   277     DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
   278   if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
   279     // Ignore the match if it's a local variable.
   280     if (d->hasLocalStorage())
   281       return;
   283     Diag.Report(d->getLocation(), stackID) << d->getType();
   284     noteInferred(d->getType(), Diag);
   285   } else if (const CXXNewExpr *expr =
   286       Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
   287     // If it's placement new, then this match doesn't count.
   288     if (isPlacementNew(expr))
   289       return;
   290     Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
   291     noteInferred(expr->getAllocatedType(), Diag);
   292   } else if (const CallExpr *expr =
   293       Result.Nodes.getNodeAs<CallExpr>("node")) {
   294     QualType badType = expr->getCallReturnType()->getPointeeType();
   295     Diag.Report(expr->getLocStart(), stackID) << badType;
   296     noteInferred(badType, Diag);
   297   }
   298 }
   300 void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
   301     DiagnosticsEngine &Diag) {
   302   unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
   303     DiagnosticIDs::Note,
   304     "%0 is a stack class because it inherits from a stack class %1");
   305   unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
   306     DiagnosticIDs::Note,
   307     "%0 is a stack class because member %1 is a stack class %2");
   309   // Find the CXXRecordDecl that is the stack class of interest
   310   while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
   311     T = arrTy->getElementType();
   312   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
   314   // Direct result, we're done.
   315   if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class"))
   316     return;
   318   const Decl *cause = inferredAllocCauses[clazz].first;
   319   if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
   320     Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
   321   } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
   322     Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
   323   }
   325   // Recursively follow this back.
   326   noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
   327 }
   329 void DiagnosticsMatcher::NonHeapClassChecker::run(
   330     const MatchFinder::MatchResult &Result) {
   331   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   332   unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
   333     DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
   334   if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
   335     // If it's placement new, then this match doesn't count.
   336     if (isPlacementNew(expr))
   337       return;
   338     Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
   339     noteInferred(expr->getAllocatedType(), Diag);
   340   } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
   341     QualType badType = expr->getCallReturnType()->getPointeeType();
   342     Diag.Report(expr->getLocStart(), stackID) << badType;
   343     noteInferred(badType, Diag);
   344   }
   345 }
   347 void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
   348     DiagnosticsEngine &Diag) {
   349   unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
   350     DiagnosticIDs::Note,
   351     "%0 is a non-heap class because it inherits from a non-heap class %1");
   352   unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
   353     DiagnosticIDs::Note,
   354     "%0 is a non-heap class because member %1 is a non-heap class %2");
   356   // Find the CXXRecordDecl that is the stack class of interest
   357   while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
   358     T = arrTy->getElementType();
   359   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
   361   // Direct result, we're done.
   362   if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class"))
   363     return;
   365   const Decl *cause = inferredAllocCauses[clazz].first;
   366   if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
   367     Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
   368   } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
   369     Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
   370   }
   372   // Recursively follow this back.
   373   noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
   374 }
   376 class MozCheckAction : public PluginASTAction {
   377 public:
   378   ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
   379     MozChecker *checker = new MozChecker(CI);
   381     ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() };
   382     return new MultiplexConsumer(consumers);
   383   }
   385   bool ParseArgs(const CompilerInstance &CI,
   386                  const std::vector<std::string> &args) {
   387     return true;
   388   }
   389 };
   390 }
   392 static FrontendPluginRegistry::Add<MozCheckAction>
   393 X("moz-check", "check moz action");

mercurial