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/Core/CFRefCount.cpp | |
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/Core/CFRefCount.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/CFRefCount.cpp | 251 |
1 files changed, 166 insertions, 85 deletions
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 { |