DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (3dc70a33491f)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ScopeChecker.h"
#include "CustomMatchers.h"

void ScopeChecker::registerMatchers(MatchFinder *AstMatcher) {
  AstMatcher->addMatcher(varDecl().bind("node"), this);
  AstMatcher->addMatcher(cxxNewExpr().bind("node"), this);
  AstMatcher->addMatcher(
      materializeTemporaryExpr(
          unless(hasDescendant(cxxConstructExpr(allowsTemporary())))
      ).bind("node"), this);
  AstMatcher->addMatcher(
      callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this);
}

// 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 ScopeChecker::check(const MatchFinder::MatchResult &Result) {
  // There are a variety of different reasons why something could be allocated
  AllocationVariety Variety = AV_None;
  SourceLocation Loc;
  QualType T;
  bool IsStaticLocal = false;

  if (const ParmVarDecl *D =
          Result.Nodes.getNodeAs<ParmVarDecl>("node")) {
    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->getBeginLoc();
    IsStaticLocal = D->isStaticLocal();
  } 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->getBeginLoc();
    }
  } 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->getBeginLoc();
  } 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->getBeginLoc();
    }
  }

  // Error messages for incorrect allocations.
  const char *Stack = "variable of type %0 only valid on the stack";
  const char *Global = "variable of type %0 only valid as global";
  const char *Heap = "variable of type %0 only valid on the heap";
  const char *NonHeap = "variable of type %0 is not valid on the heap";
  const char *NonTemporary = "variable of type %0 is not valid in a temporary";
  const char *Temporary = "variable of type %0 is only valid as a temporary";
  const char *StaticLocal = "variable of type %0 is only valid as a static "
                            "local";

  const char *StackNote =
      "value incorrectly allocated in an automatic variable";
  const char *GlobalNote = "value incorrectly allocated in a global variable";
  const char *HeapNote = "value incorrectly allocated on the heap";
  const char *TemporaryNote = "value incorrectly allocated in a temporary";

  // Report errors depending on the annotations on the input types.
  switch (Variety) {
  case AV_None:
    return;

  case AV_Global:
    StackClass.reportErrorIfPresent(*this, T, Loc, Stack, GlobalNote);
    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, GlobalNote);
    TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, GlobalNote);
    if (!IsStaticLocal) {
      StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal,
                                            GlobalNote);
    }
    break;

  case AV_Automatic:
    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, StackNote);
    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, StackNote);
    TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, StackNote);
    StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal,
                                          StackNote);
    break;

  case AV_Temporary:
    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, TemporaryNote);
    HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, TemporaryNote);
    NonTemporaryClass.reportErrorIfPresent(*this, T, Loc, NonTemporary,
                                           TemporaryNote);
    StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal,
                                          TemporaryNote);
    break;

  case AV_Heap:
    GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, HeapNote);
    StackClass.reportErrorIfPresent(*this, T, Loc, Stack, HeapNote);
    NonHeapClass.reportErrorIfPresent(*this, T, Loc, NonHeap, HeapNote);
    TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, HeapNote);
    StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal, HeapNote);
    break;
  }
}