diff options
Diffstat (limited to '')
-rw-r--r-- | build/clang-plugin/ScopeChecker.cpp | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/build/clang-plugin/ScopeChecker.cpp b/build/clang-plugin/ScopeChecker.cpp new file mode 100644 index 0000000000..962c252105 --- /dev/null +++ b/build/clang-plugin/ScopeChecker.cpp @@ -0,0 +1,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; + } +} |