ScopeChecker.cpp (7183B)
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 5 #include "ScopeChecker.h" 6 #include "CustomMatchers.h" 7 8 void ScopeChecker::registerMatchers(MatchFinder *AstMatcher) { 9 AstMatcher->addMatcher(varDecl().bind("node"), this); 10 AstMatcher->addMatcher(cxxNewExpr().bind("node"), this); 11 AstMatcher->addMatcher( 12 materializeTemporaryExpr( 13 unless(hasDescendant(cxxConstructExpr(allowsTemporary())))) 14 .bind("node"), 15 this); 16 AstMatcher->addMatcher( 17 callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this); 18 } 19 20 // These enum variants determine whether an allocation has occured in the code. 21 enum AllocationVariety { 22 AV_None, 23 AV_Global, 24 AV_Automatic, 25 AV_Temporary, 26 AV_Heap, 27 }; 28 29 // XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it 30 // probably will be used at some point in the future, in order to produce better 31 // error messages. 32 typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *> 33 AutomaticTemporaryMap; 34 AutomaticTemporaryMap AutomaticTemporaries; 35 36 void ScopeChecker::check(const MatchFinder::MatchResult &Result) { 37 // There are a variety of different reasons why something could be allocated 38 AllocationVariety Variety = AV_None; 39 SourceLocation Loc; 40 QualType T; 41 bool IsStaticLocal = false; 42 43 if (const ParmVarDecl *D = Result.Nodes.getNodeAs<ParmVarDecl>("node")) { 44 if (D->hasUnparsedDefaultArg() || D->hasUninstantiatedDefaultArg()) { 45 return; 46 } 47 if (const Expr *Default = D->getDefaultArg()) { 48 if (const MaterializeTemporaryExpr *E = 49 dyn_cast<MaterializeTemporaryExpr>(Default)) { 50 // We have just found a ParmVarDecl which has, as its default argument, 51 // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as 52 // automatic, by adding it to the AutomaticTemporaryMap. 53 // Reporting on this type will occur when the MaterializeTemporaryExpr 54 // is matched against. 55 AutomaticTemporaries[E] = D; 56 } 57 } 58 return; 59 } 60 61 // Determine the type of allocation which we detected 62 if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) { 63 if (D->hasGlobalStorage()) { 64 Variety = AV_Global; 65 } else { 66 Variety = AV_Automatic; 67 } 68 T = D->getType(); 69 Loc = D->getBeginLoc(); 70 IsStaticLocal = D->isStaticLocal(); 71 } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs<CXXNewExpr>("node")) { 72 // New allocates things on the heap. 73 // We don't consider placement new to do anything, as it doesn't actually 74 // allocate the storage, and thus gives us no useful information. 75 if (!isPlacementNew(E)) { 76 Variety = AV_Heap; 77 T = E->getAllocatedType(); 78 Loc = E->getBeginLoc(); 79 } 80 } else if (const MaterializeTemporaryExpr *E = 81 Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) { 82 // Temporaries can actually have varying storage durations, due to temporary 83 // lifetime extension. We consider the allocation variety of this temporary 84 // to be the same as the allocation variety of its lifetime. 85 86 // XXX We maybe should mark these lifetimes as being due to a temporary 87 // which has had its lifetime extended, to improve the error messages. 88 switch (E->getStorageDuration()) { 89 case SD_FullExpression: { 90 // Check if this temporary is allocated as a default argument! 91 // if it is, we want to pretend that it is automatic. 92 AutomaticTemporaryMap::iterator AutomaticTemporary = 93 AutomaticTemporaries.find(E); 94 if (AutomaticTemporary != AutomaticTemporaries.end()) { 95 Variety = AV_Automatic; 96 } else { 97 Variety = AV_Temporary; 98 } 99 } break; 100 case SD_Automatic: 101 Variety = AV_Automatic; 102 break; 103 case SD_Thread: 104 case SD_Static: 105 Variety = AV_Global; 106 break; 107 case SD_Dynamic: 108 assert(false && "I don't think that this ever should occur..."); 109 Variety = AV_Heap; 110 break; 111 } 112 T = E->getType().getUnqualifiedType(); 113 Loc = E->getBeginLoc(); 114 } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) { 115 T = E->getType()->getPointeeType(); 116 if (!T.isNull()) { 117 // This will always allocate on the heap, as the heapAllocator() check 118 // was made in the matcher 119 Variety = AV_Heap; 120 Loc = E->getBeginLoc(); 121 } 122 } 123 124 // Error messages for incorrect allocations. 125 const char *Stack = "variable of type %0 only valid on the stack"; 126 const char *Global = "variable of type %0 only valid as global"; 127 const char *Heap = "variable of type %0 only valid on the heap"; 128 const char *NonHeap = "variable of type %0 is not valid on the heap"; 129 const char *NonTemporary = "variable of type %0 is not valid in a temporary"; 130 const char *Temporary = "variable of type %0 is only valid as a temporary"; 131 const char *StaticLocal = "variable of type %0 is only valid as a static " 132 "local"; 133 134 const char *StackNote = 135 "value incorrectly allocated in an automatic variable"; 136 const char *GlobalNote = "value incorrectly allocated in a global variable"; 137 const char *HeapNote = "value incorrectly allocated on the heap"; 138 const char *TemporaryNote = "value incorrectly allocated in a temporary"; 139 140 // Report errors depending on the annotations on the input types. 141 switch (Variety) { 142 case AV_None: 143 return; 144 145 case AV_Global: 146 StackClass.reportErrorIfPresent(*this, T, Loc, Stack, GlobalNote); 147 HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, GlobalNote); 148 TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, GlobalNote); 149 if (!IsStaticLocal) { 150 StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal, 151 GlobalNote); 152 } 153 break; 154 155 case AV_Automatic: 156 GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, StackNote); 157 HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, StackNote); 158 TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, StackNote); 159 StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal, 160 StackNote); 161 break; 162 163 case AV_Temporary: 164 GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, TemporaryNote); 165 HeapClass.reportErrorIfPresent(*this, T, Loc, Heap, TemporaryNote); 166 NonTemporaryClass.reportErrorIfPresent(*this, T, Loc, NonTemporary, 167 TemporaryNote); 168 StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal, 169 TemporaryNote); 170 break; 171 172 case AV_Heap: 173 GlobalClass.reportErrorIfPresent(*this, T, Loc, Global, HeapNote); 174 StackClass.reportErrorIfPresent(*this, T, Loc, Stack, HeapNote); 175 NonHeapClass.reportErrorIfPresent(*this, T, Loc, NonHeap, HeapNote); 176 TemporaryClass.reportErrorIfPresent(*this, T, Loc, Temporary, HeapNote); 177 StaticLocalClass.reportErrorIfPresent(*this, T, Loc, StaticLocal, HeapNote); 178 break; 179 } 180 }