diff options
author | dim <dim@FreeBSD.org> | 2011-06-12 15:46:16 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2011-06-12 15:46:16 +0000 |
commit | c49018d9cce52d8c9f34b44865ec3ba8e89a1488 (patch) | |
tree | c5e9e10bc189de0058aa763c47b9920a8351b7df /lib/StaticAnalyzer | |
parent | 110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab (diff) | |
download | FreeBSD-src-c49018d9cce52d8c9f34b44865ec3ba8e89a1488.zip FreeBSD-src-c49018d9cce52d8c9f34b44865ec3ba8e89a1488.tar.gz |
Vendor import of clang trunk r132879:
http://llvm.org/svn/llvm-project/cfe/trunk@132879
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 112 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BasicStore.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BasicValueFactory.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CFRefCount.cpp | 251 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CXXExprEngine.cpp | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 17 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Environment.cpp | 27 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 34 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/FlatStore.cpp | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/GRState.cpp | 16 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ObjCMessage.cpp | 7 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/RegionStore.cpp | 26 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 13 |
16 files changed, 342 insertions, 201 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index a6a256a..f2f5c1e 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -42,6 +42,7 @@ public: bool wantsRegionChangeUpdate(const GRState *state) const; const GRState *checkRegionChanges(const GRState *state, + const StoreManager::InvalidatedSymbols *, const MemRegion * const *Begin, const MemRegion * const *End) const; @@ -77,6 +78,7 @@ public: void evalStrcmp(CheckerContext &C, const CallExpr *CE) const; void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const; void evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool isBounded = false, bool ignoreCase = false) const; @@ -711,16 +713,13 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // If the size is zero, there won't be any actual memory access, so // just bind the return value to the destination buffer and return. if (stateZeroSize) { + stateZeroSize = stateZeroSize->BindExpr(CE, destVal); C.addTransition(stateZeroSize); - if (IsMempcpy) - state->BindExpr(CE, destVal); - else - state->BindExpr(CE, sizeVal); - return; } // If the size can be nonzero, we have to check the other arguments. if (stateNonZeroSize) { + state = stateNonZeroSize; // Ensure the destination is not null. If it is NULL there will be a // NULL pointer dereference. @@ -737,42 +736,55 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, if (!state) return; - // Ensure the buffers do not overlap. - state = stateNonZeroSize; + // Ensure the accesses are valid and that the buffers do not overlap. state = CheckBufferAccess(C, state, Size, Dest, Source, /* FirstIsDst = */ true); if (Restricted) state = CheckOverlap(C, state, Size, Dest, Source); - if (state) { - - // If this is mempcpy, get the byte after the last byte copied and - // bind the expr. - if (IsMempcpy) { - loc::MemRegionVal *destRegVal = dyn_cast<loc::MemRegionVal>(&destVal); - - // Get the length to copy. - SVal lenVal = state->getSVal(Size); - NonLoc *lenValNonLoc = dyn_cast<NonLoc>(&lenVal); - + if (!state) + return; + + // If this is mempcpy, get the byte after the last byte copied and + // bind the expr. + if (IsMempcpy) { + loc::MemRegionVal *destRegVal = dyn_cast<loc::MemRegionVal>(&destVal); + assert(destRegVal && "Destination should be a known MemRegionVal here"); + + // Get the length to copy. + NonLoc *lenValNonLoc = dyn_cast<NonLoc>(&sizeVal); + + if (lenValNonLoc) { // Get the byte after the last byte copied. SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, *destRegVal, *lenValNonLoc, Dest->getType()); - + // The byte after the last byte copied is the return value. state = state->BindExpr(CE, lastElement); + } else { + // If we don't know how much we copied, we can at least + // conjure a return value for later. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SVal result = + C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + state = state->BindExpr(CE, result); } - // Invalidate the destination. - // FIXME: Even if we can't perfectly model the copy, we should see if we - // can use LazyCompoundVals to copy the source values into the destination. - // This would probably remove any existing bindings past the end of the - // copied region, but that's still an improvement over blank invalidation. - state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest)); - C.addTransition(state); + } else { + // All other copies return the destination buffer. + // (Well, bcopy() has a void return type, but this won't hurt.) + state = state->BindExpr(CE, destVal); } + + // Invalidate the destination. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // copied region, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest)); + C.addTransition(state); } } @@ -782,7 +794,7 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); const GRState *state = C.getState(); - state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true); } @@ -800,7 +812,7 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); const GRState *state = C.getState(); - state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1)); } @@ -953,7 +965,7 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { - // char *strcpy(char *restrict dst, const char *restrict src); + // char *strncpy(char *restrict dst, const char *restrict src, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isBounded = */ true, @@ -1128,6 +1140,12 @@ void CStringChecker::evalStrcasecmp(CheckerContext &C, evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); } +void CStringChecker::evalStrncasecmp(CheckerContext &C, + const CallExpr *CE) const { + //int strncasecmp(const char *restrict s1, const char *restrict s2, size_t n); + evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true); +} + void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool isBounded, bool ignoreCase) const { const GRState *state = C.getState(); @@ -1181,31 +1199,17 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, return; llvm::APSInt lenInt(CI->getValue()); - // Compare using the bounds provided like strncmp() does. - if (ignoreCase) { - // TODO Implement compare_lower(RHS, n) in LLVM StringRef. - // result = s1StrRef.compare_lower(s2StrRef, - // (size_t)lenInt.getLimitedValue()); + // Create substrings of each to compare the prefix. + s1StrRef = s1StrRef.substr(0, (size_t)lenInt.getLimitedValue()); + s2StrRef = s2StrRef.substr(0, (size_t)lenInt.getLimitedValue()); + } - // For now, give up. - return; - } else { - // Create substrings of each to compare the prefix. - llvm::StringRef s1SubStr = - s1StrRef.substr(0, (size_t)lenInt.getLimitedValue()); - llvm::StringRef s2SubStr = - s2StrRef.substr(0, (size_t)lenInt.getLimitedValue()); - - // Compare the substrings. - result = s1SubStr.compare(s2SubStr); - } + if (ignoreCase) { + // Compare string 1 to string 2 the same way strcasecmp() does. + result = s1StrRef.compare_lower(s2StrRef); } else { // Compare string 1 to string 2 the same way strcmp() does. - if (ignoreCase) { - result = s1StrRef.compare_lower(s2StrRef); - } else { - result = s1StrRef.compare(s2StrRef); - } + result = s1StrRef.compare(s2StrRef); } // Build the SVal of the comparison to bind the return value. @@ -1243,11 +1247,11 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) .Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy) - .Case("mempcpy", &CStringChecker::evalMempcpy) + .Cases("mempcpy", "__mempcpy_chk", &CStringChecker::evalMempcpy) .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) - .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) + //.Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) .Cases("strcat", "__strcat_chk", &CStringChecker::evalStrcat) .Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat) @@ -1256,6 +1260,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { .Case("strcmp", &CStringChecker::evalStrcmp) .Case("strncmp", &CStringChecker::evalStrncmp) .Case("strcasecmp", &CStringChecker::evalStrcasecmp) + .Case("strncasecmp", &CStringChecker::evalStrncasecmp) .Case("bcopy", &CStringChecker::evalBcopy) .Default(NULL); @@ -1311,6 +1316,7 @@ bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) const { const GRState * CStringChecker::checkRegionChanges(const GRState *state, + const StoreManager::InvalidatedSymbols *, const MemRegion * const *Begin, const MemRegion * const *End) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 63a5917..a51d8e0 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -97,7 +97,7 @@ public: void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, AnalysisManager &mgr, BugReporter &BR) const { - if (!D->isThisDeclarationADefinition()) + if (!D->doesThisDeclarationHaveABody()) return; if (!D->getResultType()->isVoidType()) return; diff --git a/lib/StaticAnalyzer/Core/BasicStore.cpp b/lib/StaticAnalyzer/Core/BasicStore.cpp index 4faa84c..7c9f45a 100644 --- a/lib/StaticAnalyzer/Core/BasicStore.cpp +++ b/lib/StaticAnalyzer/Core/BasicStore.cpp @@ -49,11 +49,11 @@ public: SVal Retrieve(Store store, Loc loc, QualType T = QualType()); StoreRef invalidateRegion(Store store, const MemRegion *R, const Expr *E, - unsigned Count, InvalidatedSymbols *IS); + unsigned Count, InvalidatedSymbols &IS); StoreRef invalidateRegions(Store store, const MemRegion * const *Begin, const MemRegion * const *End, const Expr *E, - unsigned Count, InvalidatedSymbols *IS, + unsigned Count, InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions); @@ -538,7 +538,7 @@ StoreRef BasicStoreManager::invalidateRegions(Store store, const MemRegion * const *I, const MemRegion * const *End, const Expr *E, unsigned Count, - InvalidatedSymbols *IS, + InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions) { StoreRef newStore(store, *this); @@ -587,18 +587,16 @@ StoreRef BasicStoreManager::invalidateRegion(Store store, const MemRegion *R, const Expr *E, unsigned Count, - InvalidatedSymbols *IS) { + InvalidatedSymbols &IS) { R = R->StripCasts(); if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) return StoreRef(store, *this); - if (IS) { - BindingsTy B = GetBindings(store); - if (BindingsTy::data_type *Val = B.lookup(R)) { - if (SymbolRef Sym = Val->getAsSymbol()) - IS->insert(Sym); - } + BindingsTy B = GetBindings(store); + if (BindingsTy::data_type *Val = B.lookup(R)) { + if (SymbolRef Sym = Val->getAsSymbol()) + IS.insert(Sym); } QualType T = cast<TypedRegion>(R)->getValueType(); diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index ae8a04c..0ed4ff1 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -101,7 +101,8 @@ const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { unsigned bits = Ctx.getTypeSize(T); - llvm::APSInt V(bits, T->isUnsignedIntegerType() || Loc::isLocType(T)); + llvm::APSInt V(bits, + T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T)); V = X; return getValue(V); } diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index d9b1ce8..0512e2f 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -930,6 +930,13 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { S = getPersistentStopSummary(); break; } + // For C++ methods, generate an implicit "stop" summary as well. We + // can relax this once we have a clear policy for C++ methods and + // ownership attributes. + if (isa<CXXMethodDecl>(FD)) { + S = getPersistentStopSummary(); + break; + } // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the // function's type. @@ -1111,15 +1118,11 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { RetainSummary* RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, StringRef FName) { - if (FName.find("Create") != StringRef::npos || FName.find("Copy") != StringRef::npos) return getCFSummaryCreateRule(FD); - if (FName.find("Get") != StringRef::npos) - return getCFSummaryGetRule(FD); - - return getDefaultSummary(); + return getCFSummaryGetRule(FD); } RetainSummary* @@ -1233,6 +1236,9 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, if (FD->getAttr<CFReturnsRetainedAttr>()) { Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); } + else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { + Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + } } } @@ -1758,6 +1764,15 @@ public: StmtNodeBuilder& Builder, const ReturnStmt* S, ExplodedNode* Pred); + + void evalReturnWithRetEffect(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const ReturnStmt* S, + ExplodedNode* Pred, + RetEffect RE, RefVal X, + SymbolRef Sym, const GRState *state); + // Assumptions. @@ -2075,7 +2090,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, } if (CurrV.isOwned()) { - os << "+1 retain count (owning reference)."; + os << "+1 retain count"; if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) { assert(CurrV.getObjKind() == RetEffect::CF); @@ -2085,7 +2100,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, } else { assert (CurrV.isNotOwned()); - os << "+0 retain count (non-owning reference)."; + os << "+0 retain count"; } PathDiagnosticLocation Pos(S, BRC.getSourceManager()); @@ -2217,11 +2232,11 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, case RefVal::ReturnedOwned: os << "Object returned to caller as an owning reference (single retain " - "count transferred to caller)."; + "count transferred to caller)"; break; case RefVal::ReturnedNotOwned: - os << "Object returned to caller with a +0 (non-owning) retain count."; + os << "Object returned to caller with a +0 retain count"; break; default: @@ -2354,12 +2369,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, llvm::tie(AllocNode, FirstBinding) = GetAllocationSite(BRC.getStateManager(), EndN, Sym); - // Get the allocate site. - assert(AllocNode); - const Stmt* FirstStmt = cast<PostStmt>(AllocNode->getLocation()).getStmt(); - SourceManager& SMgr = BRC.getSourceManager(); - unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart()); // Compute an actual location for the leak. Sometimes a leak doesn't // occur at an actual statement (e.g., transition between blocks; end @@ -2392,10 +2402,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << "Object allocated on line " << AllocLine; + os << "Object leaked: "; - if (FirstBinding) - os << " and stored into '" << FirstBinding->getString() << '\''; + if (FirstBinding) { + os << "object allocated and stored into '" + << FirstBinding->getString() << '\''; + } + else + os << "allocated object"; // Get the retain count. const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); @@ -2404,12 +2418,22 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, // FIXME: Per comments in rdar://6320065, "create" only applies to CF // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. - ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); - os << " is returned from a method whose name ('" - << MD.getSelector().getAsString() - << "') does not contain 'copy' or otherwise starts with" - " 'new' or 'alloc'. This violates the naming convention rules given" - " in the Memory Management Guide for Cocoa (object leaked)"; + const Decl *D = &EndN->getCodeDecl(); + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + os << " is returned from a method whose name ('" + << MD->getSelector().getAsString() + << "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'." + " This violates the naming convention rules " + " given in the Memory Management Guide for Cocoa"; + } + else { + const FunctionDecl *FD = cast<FunctionDecl>(D); + os << " is return from a function whose name ('" + << FD->getNameAsString() + << "') does not contain 'Copy' or 'Create'. This violates the naming" + " convention rules given the Memory Management Guide for Core " + " Foundation"; + } } else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); @@ -2421,7 +2445,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, } else os << " is not referenced later in this execution path and has a retain " - "count of +" << RV->getCount() << " (object leaked)"; + "count of +" << RV->getCount(); return new PathDiagnosticEventPiece(L, os.str()); } @@ -2494,6 +2518,23 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { return RetTy; } + +// HACK: Symbols that have ref-count state that are referenced directly +// (not as structure or array elements, or via bindings) by an argument +// should not have their ref-count state stripped after we have +// done an invalidation pass. +// +// FIXME: This is a global to currently share between CFRefCount and +// RetainReleaseChecker. Eventually all functionality in CFRefCount should +// be migrated to RetainReleaseChecker, and we can make this a non-global. +llvm::DenseSet<SymbolRef> WhitelistedSymbols; +namespace { +struct ResetWhiteList { + ResetWhiteList() {} + ~ResetWhiteList() { WhitelistedSymbols.clear(); } +}; +} + void CFRefCount::evalSummary(ExplodedNodeSet& Dst, ExprEngine& Eng, StmtNodeBuilder& Builder, @@ -2510,12 +2551,9 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, SymbolRef ErrorSym = 0; llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; - - // HACK: Symbols that have ref-count state that are referenced directly - // (not as structure or array elements, or via bindings) by an argument - // should not have their ref-count state stripped after we have - // done an invalidation pass. - llvm::DenseSet<SymbolRef> WhitelistedSymbols; + + // Use RAII to make sure the whitelist is properly cleared. + ResetWhiteList resetWhiteList; // Invalidate all instance variables of the receiver of a message. // FIXME: We should be able to do better with inter-procedural analysis. @@ -2624,20 +2662,14 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate // global variables. - state = state->invalidateRegions(RegionsToInvalidate.data(), - RegionsToInvalidate.data() + - RegionsToInvalidate.size(), - Ex, Count, &IS, - /* invalidateGlobals = */ true); - - for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), - E = IS.end(); I!=E; ++I) { - SymbolRef sym = *I; - if (WhitelistedSymbols.count(sym)) - continue; - // Remove any existing reference-count binding. - state = state->remove<RefBindings>(*I); - } + // NOTE: RetainReleaseChecker handles the actual invalidation of symbols. + state = + state->invalidateRegions(RegionsToInvalidate.data(), + RegionsToInvalidate.data() + + RegionsToInvalidate.size(), + Ex, Count, &IS, + /* invalidateGlobals = */ + Eng.doesInvalidateGlobals(callOrMsg)); // Evaluate the effect on the message receiver. if (!ErrorRange.isValid() && Receiver) { @@ -2946,30 +2978,50 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst, assert(T); X = *T; + // Consult the summary of the enclosing method. + Decl const *CD = &Pred->getCodeDecl(); + + if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { + const RetainSummary &Summ = *Summaries.getMethodSummary(MD); + return evalReturnWithRetEffect(Dst, Eng, Builder, S, + Pred, Summ.getRetEffect(), X, + Sym, state); + } + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { + if (!isa<CXXMethodDecl>(FD)) + if (const RetainSummary *Summ = Summaries.getSummary(FD)) + return evalReturnWithRetEffect(Dst, Eng, Builder, S, + Pred, Summ->getRetEffect(), X, + Sym, state); + } +} + +void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst, + ExprEngine &Eng, + StmtNodeBuilder &Builder, + const ReturnStmt *S, + ExplodedNode *Pred, + RetEffect RE, RefVal X, + SymbolRef Sym, const GRState *state) { // Any leaks or other errors? if (X.isReturnedOwned() && X.getCount() == 0) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - RetEffect RE = Summ.getRetEffect(); + if (RE.getKind() != RetEffect::NoRet) { bool hasError = false; - - if (RE.getKind() != RetEffect::NoRet) { - if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { - // Things are more complicated with garbage collection. If the - // returned object is suppose to be an Objective-C object, we have - // a leak (as the caller expects a GC'ed object) because no - // method should return ownership unless it returns a CF object. - hasError = true; - X = X ^ RefVal::ErrorGCLeakReturned; - } - else if (!RE.isOwned()) { - // Either we are using GC and the returned object is a CF type - // or we aren't using GC. In either case, we expect that the - // enclosing method is expected to return ownership. - hasError = true; - X = X ^ RefVal::ErrorLeakReturned; - } + if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { + // Things are more complicated with garbage collection. If the + // returned object is suppose to be an Objective-C object, we have + // a leak (as the caller expects a GC'ed object) because no + // method should return ownership unless it returns a CF object. + hasError = true; + X = X ^ RefVal::ErrorGCLeakReturned; + } + else if (!RE.isOwned()) { + // Either we are using GC and the returned object is a CF type + // or we aren't using GC. In either case, we expect that the + // enclosing method is expected to return ownership. + hasError = true; + X = X ^ RefVal::ErrorLeakReturned; } if (hasError) { @@ -2987,26 +3039,24 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst, } } } + return; } - else if (X.isReturnedNotOwned()) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - if (Summ.getRetEffect().isOwned()) { - // Trying to return a not owned object to a caller expecting an - // owned object. - - static int ReturnNotOwnedForOwnedTag = 0; - state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); - if (ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnNotOwnedForOwnedTag), - state, Pred)) { - CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), - *this, N, Sym); - BR->EmitReport(report); - } + + if (X.isReturnedNotOwned()) { + if (RE.isOwned()) { + // Trying to return a not owned object to a caller expecting an + // owned object. + + static int ReturnNotOwnedForOwnedTag = 0; + state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); + if (ExplodedNode *N = + Builder.generateNode(PostStmt(S, Pred->getLocationContext(), + &ReturnNotOwnedForOwnedTag), + state, Pred)) { + CFRefReport *report = + new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), + *this, N, Sym); + BR->EmitReport(report); } } } @@ -3418,12 +3468,43 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, namespace { class RetainReleaseChecker - : public Checker< check::PostStmt<BlockExpr> > { + : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > { public: + bool wantsRegionUpdate; + + RetainReleaseChecker() : wantsRegionUpdate(true) {} + + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + const GRState *checkRegionChanges(const GRState *state, + const StoreManager::InvalidatedSymbols *invalidated, + const MemRegion * const *begin, + const MemRegion * const *end) const; + + bool wantsRegionChangeUpdate(const GRState *state) const { + return wantsRegionUpdate; + } }; } // end anonymous namespace +const GRState * +RetainReleaseChecker::checkRegionChanges(const GRState *state, + const StoreManager::InvalidatedSymbols *invalidated, + const MemRegion * const *begin, + const MemRegion * const *end) const { + if (!invalidated) + return state; + + for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + E = invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // Remove any existing reference-count binding. + state = state->remove<RefBindings>(sym); + } + return state; +} void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { diff --git a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp b/lib/StaticAnalyzer/Core/CXXExprEngine.cpp index 54cbca0..ef7bc20 100644 --- a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/CXXExprEngine.cpp @@ -132,7 +132,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, assert(CD); #if 0 - if (!(CD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) + if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) // FIXME: invalidate the object. return; #endif @@ -246,7 +246,7 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - if (!(DD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) + if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) return; // Create the context for 'this' region. const StackFrameContext *SFC = AMgr.getStackFrame(DD, diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 4a25490..ba7c384 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -345,6 +345,7 @@ bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { /// \brief Run checkers for region changes. const GRState * CheckerManager::runCheckersForRegionChanges(const GRState *state, + const StoreManager::InvalidatedSymbols *invalidated, const MemRegion * const *Begin, const MemRegion * const *End) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { @@ -352,7 +353,7 @@ CheckerManager::runCheckersForRegionChanges(const GRState *state, // bail out. if (!state) return NULL; - state = RegionChangesCheckers[i].CheckFn(state, Begin, End); + state = RegionChangesCheckers[i].CheckFn(state, invalidated, Begin, End); } return state; } @@ -415,6 +416,15 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } } +/// \brief Run checkers for the entire Translation Unit. +void CheckerManager::runCheckersOnEndOfTranslationUnit( + const TranslationUnitDecl *TU, + AnalysisManager &mgr, + BugReporter &BR) { + for (unsigned i = 0, e = EndOfTranslationUnitCheckers.size(); i != e; ++i) + EndOfTranslationUnitCheckers[i](TU, mgr, BR); +} + //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// @@ -494,6 +504,11 @@ void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { EvalCallCheckers.push_back(checkfn); } +void CheckerManager::_registerForEndOfTranslationUnit( + CheckEndOfTranslationUnit checkfn) { + EndOfTranslationUnitCheckers.push_back(checkfn); +} + //===----------------------------------------------------------------------===// // Implementation details. //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index a00f9dc1..48f126b 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -39,6 +39,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, } for (;;) { + if (const Expr *Ex = dyn_cast<Expr>(E)) + E = Ex->IgnoreParens(); + switch (E->getStmtClass()) { case Stmt::AddrLabelExprClass: return svalBuilder.makeLoc(cast<AddrLabelExpr>(E)); @@ -48,13 +51,10 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, continue; } case Stmt::ParenExprClass: - // ParenExprs are no-ops. - E = cast<ParenExpr>(E)->getSubExpr(); - continue; case Stmt::GenericSelectionExprClass: - // GenericSelectionExprs are no-ops. - E = cast<GenericSelectionExpr>(E)->getResultExpr(); - continue; + llvm_unreachable("ParenExprs and GenericSelectionExprs should " + "have been handled by IgnoreParens()"); + return UnknownVal(); case Stmt::CharacterLiteralClass: { const CharacterLiteral* C = cast<CharacterLiteral>(E); return svalBuilder.makeIntVal(C->getValue(), C->getType()); @@ -77,21 +77,6 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, // For special C0xx nullptr case, make a null pointer SVal. case Stmt::CXXNullPtrLiteralExprClass: return svalBuilder.makeNull(); - case Stmt::ImplicitCastExprClass: - case Stmt::CXXFunctionalCastExprClass: - case Stmt::CStyleCastExprClass: { - // We blast through no-op casts to get the descendant - // subexpression that has a value. - const CastExpr* C = cast<CastExpr>(E); - QualType CT = C->getType(); - if (CT->isVoidType()) - return UnknownVal(); - if (C->getCastKind() == CK_NoOp) { - E = C->getSubExpr(); - continue; - } - break; - } case Stmt::ExprWithCleanupsClass: E = cast<ExprWithCleanups>(E)->getSubExpr(); continue; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 657420d..aed39eb 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -156,6 +156,27 @@ const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) { return state; } +bool +ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const +{ + if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) { + SVal calleeV = callOrMessage.getFunctionCallee(); + if (const FunctionTextRegion *codeR = + llvm::dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) { + + const FunctionDecl *fd = codeR->getDecl(); + if (const IdentifierInfo *ii = fd->getIdentifier()) { + llvm::StringRef fname = ii->getName(); + if (fname == "strlen") + return false; + } + } + } + + // The conservative answer: invalidates globals. + return true; +} + //===----------------------------------------------------------------------===// // Top-level transfer function logic (Dispatcher). //===----------------------------------------------------------------------===// @@ -179,9 +200,11 @@ bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) { const GRState * ExprEngine::processRegionChanges(const GRState *state, - const MemRegion * const *Begin, - const MemRegion * const *End) { - return getCheckerManager().runCheckersForRegionChanges(state, Begin, End); + const StoreManager::InvalidatedSymbols *invalidated, + const MemRegion * const *Begin, + const MemRegion * const *End) { + return getCheckerManager().runCheckersForRegionChanges(state, invalidated, + Begin, End); } void ExprEngine::processEndWorklist(bool hasWorkRemaining) { @@ -516,6 +539,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::VAArgExprClass: case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: + case Stmt::AsTypeExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need @@ -2462,7 +2486,7 @@ void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE, const APSInt &IV = Res.Val.getInt(); assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); assert(OOE->getType()->isIntegerType()); - assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); SVal X = svalBuilder.makeIntVal(IV); MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X)); return; @@ -2701,7 +2725,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, if (U->isLValue()) state = state->BindExpr(U, loc); else - state = state->BindExpr(U, V2); + state = state->BindExpr(U, U->isPostfix() ? V2 : Result); // Perform the store. evalStore(Dst, NULL, U, *I2, state, loc, Result); diff --git a/lib/StaticAnalyzer/Core/FlatStore.cpp b/lib/StaticAnalyzer/Core/FlatStore.cpp index 7bdca6b..ca867ae 100644 --- a/lib/StaticAnalyzer/Core/FlatStore.cpp +++ b/lib/StaticAnalyzer/Core/FlatStore.cpp @@ -59,7 +59,7 @@ public: StoreRef invalidateRegions(Store store, const MemRegion * const *I, const MemRegion * const *E, const Expr *Ex, - unsigned Count, InvalidatedSymbols *IS, + unsigned Count, InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions); @@ -175,7 +175,7 @@ StoreRef FlatStoreManager::invalidateRegions(Store store, const MemRegion * const *I, const MemRegion * const *E, const Expr *Ex, unsigned Count, - InvalidatedSymbols *IS, + InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions) { assert(false && "Not implemented"); diff --git a/lib/StaticAnalyzer/Core/GRState.cpp b/lib/StaticAnalyzer/Core/GRState.cpp index 7b21677..0f6ff1e 100644 --- a/lib/StaticAnalyzer/Core/GRState.cpp +++ b/lib/StaticAnalyzer/Core/GRState.cpp @@ -141,6 +141,20 @@ const GRState *GRState::invalidateRegions(const MemRegion * const *Begin, const Expr *E, unsigned Count, StoreManager::InvalidatedSymbols *IS, bool invalidateGlobals) const { + if (!IS) { + StoreManager::InvalidatedSymbols invalidated; + return invalidateRegionsImpl(Begin, End, E, Count, + invalidated, invalidateGlobals); + } + return invalidateRegionsImpl(Begin, End, E, Count, *IS, invalidateGlobals); +} + +const GRState * +GRState::invalidateRegionsImpl(const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + StoreManager::InvalidatedSymbols &IS, + bool invalidateGlobals) const { GRStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); @@ -150,7 +164,7 @@ const GRState *GRState::invalidateRegions(const MemRegion * const *Begin, = Mgr.StoreMgr->invalidateRegions(getStore(), Begin, End, E, Count, IS, invalidateGlobals, &Regions); const GRState *newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, + return Eng->processRegionChanges(newState, &IS, &Regions.front(), &Regions.back()+1); } diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp index c005819..c000600 100644 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp @@ -141,6 +141,13 @@ SVal CallOrObjCMessage::getArgSValAsScalarOrLoc(unsigned i) const { return UnknownVal(); } +SVal CallOrObjCMessage::getFunctionCallee() const { + assert(isFunctionCall()); + assert(!isCXXCall()); + const Expr *callee = CallE->getCallee()->IgnoreParenCasts(); + return State->getSVal(callee); +} + SVal CallOrObjCMessage::getCXXCallee() const { assert(isCXXCall()); const Expr *callee = diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 4522f97..d0d8f60 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -240,7 +240,7 @@ public: const MemRegion * const *Begin, const MemRegion * const *End, const Expr *E, unsigned Count, - InvalidatedSymbols *IS, + InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions); @@ -586,14 +586,14 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> { const Expr *Ex; unsigned Count; - StoreManager::InvalidatedSymbols *IS; + StoreManager::InvalidatedSymbols &IS; StoreManager::InvalidatedRegions *Regions; public: invalidateRegionsWorker(RegionStoreManager &rm, GRStateManager &stateMgr, RegionBindings b, const Expr *ex, unsigned count, - StoreManager::InvalidatedSymbols *is, + StoreManager::InvalidatedSymbols &is, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), @@ -609,9 +609,8 @@ private: void invalidateRegionsWorker::VisitBinding(SVal V) { // A symbol? Mark it touched by the invalidation. - if (IS) - if (SymbolRef Sym = V.getAsSymbol()) - IS->insert(Sym); + if (SymbolRef Sym = V.getAsSymbol()) + IS.insert(Sym); if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); @@ -648,11 +647,9 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, } void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { - if (IS) { - // Symbolic region? Mark that symbol touched by the invalidation. - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) - IS->insert(SR->getSymbol()); - } + // Symbolic region? Mark that symbol touched by the invalidation. + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) + IS.insert(SR->getSymbol()); // BlockDataRegion? If so, invalidate captured variables that are passed // by reference. @@ -724,7 +721,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, const MemRegion * const *I, const MemRegion * const *E, const Expr *Ex, unsigned Count, - InvalidatedSymbols *IS, + InvalidatedSymbols &IS, bool invalidateGlobals, InvalidatedRegions *Regions) { invalidateRegionsWorker W(*this, StateMgr, @@ -1066,6 +1063,11 @@ SVal RegionStoreManager::RetrieveElement(Store store, // return *y; // FIXME: This is a hack, and doesn't do anything really intelligent yet. const RegionRawOffset &O = R->getAsArrayOffset(); + + // If we cannot reason about the offset, return an unknown value. + if (!O.getRegion()) + return UnknownVal(); + if (const TypedRegion *baseR = dyn_cast_or_null<TypedRegion>(O.getRegion())) { QualType baseT = baseR->getValueType(); if (baseT->isScalarType()) { diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 1ee694e..20762e0 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -262,7 +262,8 @@ const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state, QualType T = Sym->getType(Ctx); assert(T->isIntegerType() || Loc::isLocType(T)); unsigned bitwidth = Ctx.getTypeSize(T); - bool isSymUnsigned = T->isUnsignedIntegerType() || Loc::isLocType(T); + bool isSymUnsigned + = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T); // Convert the adjustment. Adjustment.setIsUnsigned(isSymUnsigned); diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 5d80251..197442b 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -97,7 +97,8 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { return UnknownVal(); llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::isLocType(castTy)); + i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || + Loc::isLocType(castTy)); i = i.extOrTrunc(Context.getTypeSize(castTy)); if (isLocType) @@ -129,7 +130,8 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { return makeLocAsInteger(val, BitWidth); llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::isLocType(castTy)); + i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() || + Loc::isLocType(castTy)); i = i.extOrTrunc(BitWidth); return makeIntVal(i); } diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fe6e1fd..b8dbb54 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -200,18 +200,20 @@ void AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) { } break; } - + + case Decl::ObjCCategoryImpl: case Decl::ObjCImplementation: { - ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I); + ObjCImplDecl* ID = cast<ObjCImplDecl>(*I); HandleCode(ID); - for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(), + for (ObjCContainerDecl::method_iterator MI = ID->meth_begin(), ME = ID->meth_end(); MI != ME; ++MI) { checkerMgr->runCheckersOnASTDecl(*MI, *Mgr, BR); if ((*MI)->isThisDeclarationADefinition()) { if (!Opts.AnalyzeSpecificFunction.empty() && - Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString()) + Opts.AnalyzeSpecificFunction != + (*MI)->getSelector().getAsString()) break; DisplayFunction(*MI); HandleCode(*MI); @@ -232,6 +234,9 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); HandleDeclContext(C, TU); + // After all decls handled, run checkers on the entire TranslationUnit. + checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + // Explicitly destroy the PathDiagnosticClient. This will flush its output. // FIXME: This should be replaced with something that doesn't rely on // side-effects in PathDiagnosticClient's destructor. This is required when |