diff options
author | Anna Zaks <ganna@apple.com> | 2013-04-10 21:42:06 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2013-04-10 21:42:06 +0000 |
commit | 7a87e520e42c1e58c358e3a9a436ef17f551fd13 (patch) | |
tree | 4462e7eba5edfea7dbb7a7a4f8878c7168866e33 /lib/StaticAnalyzer | |
parent | 27d99dd714895564b526b786284a46b40f53be01 (diff) |
[analyzer] When reporting a leak in RetainCount checker due to an early exit from init, step into init.
The heuristic here (proposed by Jordan) is that, usually, if a leak is due to an early exit from init, the allocation site will be
a call to alloc. Note that in other cases init resets self to [super init], which becomes the allocation site of the object.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179221 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 77 |
1 files changed, 63 insertions, 14 deletions
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index fa864b44e7..f6168457a2 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -2134,39 +2134,82 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the // value is only reported if the allocation occurred in the same function as -// the leak. -static std::pair<const ExplodedNode*,const MemRegion*> +// the leak. The function can also return a location context, which should be +// treated as interesting. +struct AllocationInfo { + const ExplodedNode* N; + const MemRegion* R; + const LocationContext *InterestingMethodContext; + AllocationInfo(const ExplodedNode* InN, + const MemRegion* InR, + const LocationContext *InInterestingMethodContext) : + N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} +}; + +static AllocationInfo GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, SymbolRef Sym) { - const ExplodedNode *Last = N; + const ExplodedNode *AllocationNode = N; + const ExplodedNode *AllocationNodeInCurrentContext = N; const MemRegion* FirstBinding = 0; const LocationContext *LeakContext = N->getLocationContext(); + // The location context of the init method called on the leaked object, if + // available. + const LocationContext *InitMethodContext = 0; + while (N) { ProgramStateRef St = N->getState(); + const LocationContext *NContext = N->getLocationContext(); if (!getRefBinding(St, Sym)) break; StoreManager::FindUniqueBinding FB(Sym); StateMgr.iterBindings(St, FB); + if (FB) { const MemRegion *R = FB.getRegion(); const VarRegion *VR = R->getAs<VarRegion>(); // Do not show local variables belonging to a function other than // where the error is reported. if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame()) - FirstBinding = R; + FirstBinding = R; } - // Allocation node, is the last node in the current context in which the - // symbol was tracked. - if (N->getLocationContext() == LeakContext) - Last = N; + // AllocationNode is the last node in which the symbol was tracked. + AllocationNode = N; + + // AllocationNodeInCurrentContext, is the last node in the current context + // in which the symbol was tracked. + if (NContext == LeakContext) + AllocationNodeInCurrentContext = N; + + // If init was called on the given symbol, store the init method's location + // context. + if (Optional<CallEnter> CEP = N->getLocation().getAs<CallEnter>()) { + const Stmt *CE = CEP->getCallExpr(); + if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(CE)) { + SVal RecVal = St->getSVal(ME->getInstanceReceiver(), NContext); + if (RecVal.getAsSymbol() == Sym && ME->getMethodFamily() == OMF_init) + InitMethodContext = CEP->getCalleeContext(); + } + } N = N->pred_empty() ? NULL : *(N->pred_begin()); } + // If we are reporting a leak of the object that was allocated with alloc, + // mark it's init method as interesting. + const LocationContext *InterestingMethodContext = 0; + if (InitMethodContext) { + const ProgramPoint AllocPP = AllocationNode->getLocation(); + if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) + if (ME->getMethodFamily() == OMF_alloc) + InterestingMethodContext = InitMethodContext; + } + // If allocation happened in a function different from the leak node context, // do not report the binding. assert(N && "Could not find allocation node"); @@ -2174,7 +2217,9 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, FirstBinding = 0; } - return std::make_pair(Last, FirstBinding); + return AllocationInfo(AllocationNodeInCurrentContext, + FirstBinding, + InterestingMethodContext); } PathDiagnosticPiece* @@ -2197,12 +2242,12 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. - const ExplodedNode *AllocNode = 0; - const MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = + AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); + const MemRegion* FirstBinding = AllocI.R; + BR.markInteresting(AllocI.InterestingMethodContext); + SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't @@ -2289,9 +2334,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, const SourceManager& SMgr = Ctx.getSourceManager(); - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. + AllocationInfo AllocI = GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + AllocNode = AllocI.N; + AllocBinding = AllocI.R; + markInteresting(AllocI.InterestingMethodContext); + // Get the SourceLocation for the allocation site. // FIXME: This will crash the analyzer if an allocation comes from an // implicit call. (Currently there are no such allocations in Cocoa, though.) |