From 7fa9b4f258636d89342eda28f21a986c8ac353b1 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 1 Jun 2012 20:04:04 +0000 Subject: [PATCH] static analyzer: add inlining support for directly called blocks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157833 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Analysis/AnalysisContext.h | 30 +- .../Core/PathSensitive/ExprEngine.h | 2 +- .../Core/PathSensitive/SymbolManager.h | 5 +- lib/Analysis/AnalysisDeclContext.cpp | 41 +- .../Core/ExprEngineCallAndReturn.cpp | 99 ++-- lib/StaticAnalyzer/Core/MemRegion.cpp | 39 +- lib/StaticAnalyzer/Core/ProgramState.cpp | 10 + lib/StaticAnalyzer/Core/RegionStore.cpp | 60 +- test/Analysis/blocks.m | 4 +- test/Analysis/inline-plist.c | 550 +++++++++++++++++- test/Analysis/retain-release.m | 3 +- 11 files changed, 757 insertions(+), 86 deletions(-) diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h index 4e2a544330..46b4e93bb7 100644 --- a/include/clang/Analysis/AnalysisContext.h +++ b/include/clang/Analysis/AnalysisContext.h @@ -38,6 +38,7 @@ class PseudoConstantAnalysis; class ImplicitParamDecl; class LocationContextManager; class StackFrameContext; +class BlockInvocationContext; class AnalysisDeclContextManager; class LocationContext; @@ -162,6 +163,11 @@ public: const Stmt *S, const CFGBlock *Blk, unsigned Idx); + + const BlockInvocationContext * + getBlockInvocationContext(const LocationContext *parent, + const BlockDecl *BD, + const void *ContextData); /// Return the specified analysis object, lazily running the analysis if /// necessary. Return NULL if the analysis could not run. @@ -227,8 +233,6 @@ public: } const StackFrameContext *getCurrentStackFrame() const; - const StackFrameContext * - getStackFrameForDeclContext(const DeclContext *DC) const; virtual void Profile(llvm::FoldingSetNodeID &ID) = 0; @@ -307,27 +311,32 @@ public: }; class BlockInvocationContext : public LocationContext { - // FIXME: Add back context-sensivity (we don't want libAnalysis to know - // about MemRegion). const BlockDecl *BD; + + // FIXME: Come up with a more type-safe way to model context-sensitivity. + const void *ContextData; friend class LocationContextManager; BlockInvocationContext(AnalysisDeclContext *ctx, const LocationContext *parent, - const BlockDecl *bd) - : LocationContext(Block, ctx, parent), BD(bd) {} + const BlockDecl *bd, const void *contextData) + : LocationContext(Block, ctx, parent), BD(bd), ContextData(contextData) {} public: ~BlockInvocationContext() {} const BlockDecl *getBlockDecl() const { return BD; } + + const void *getContextData() const { return ContextData; } void Profile(llvm::FoldingSetNodeID &ID); static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, - const LocationContext *parent, const BlockDecl *bd) { + const LocationContext *parent, const BlockDecl *bd, + const void *contextData) { ProfileCommon(ID, Block, ctx, parent, bd); + ID.AddPointer(contextData); } static bool classof(const LocationContext *Ctx) { @@ -348,6 +357,12 @@ public: const ScopeContext *getScope(AnalysisDeclContext *ctx, const LocationContext *parent, const Stmt *s); + + const BlockInvocationContext * + getBlockInvocationContext(AnalysisDeclContext *ctx, + const LocationContext *parent, + const BlockDecl *BD, + const void *ContextData); /// Discard all previously created LocationContext objects. void clear(); @@ -404,7 +419,6 @@ public: return LocContexts.getStackFrame(getContext(D), Parent, S, Blk, Idx); } - /// Discard all previously created AnalysisDeclContexts. void clear(); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 73f39b1bce..14087c926d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -494,7 +494,7 @@ private: ProgramStateRef St, SVal location, const ProgramPointTag *tag, bool isLoad); - bool shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred); + bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred); bool InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 8431b0e739..5d27f8654e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -572,7 +572,7 @@ class SymbolReaper { RegionSetTy RegionRoots; - const LocationContext *LCtx; + const StackFrameContext *LCtx; const Stmt *Loc; SymbolManager& SymMgr; StoreRef reapedStore; @@ -586,7 +586,8 @@ public: /// considered live. SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr, StoreManager &storeMgr) - : LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {} + : LCtx(ctx->getCurrentStackFrame()), Loc(s), SymMgr(symmgr), + reapedStore(0, storeMgr) {} ~SymbolReaper() {} diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index 624373330f..b618569cd7 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -204,6 +204,14 @@ AnalysisDeclContext::getStackFrame(LocationContext const *Parent, const Stmt *S, return getLocationContextManager().getStackFrame(this, Parent, S, Blk, Idx); } +const BlockInvocationContext * +AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, + const clang::BlockDecl *BD, + const void *ContextData) { + return getLocationContextManager().getBlockInvocationContext(this, parent, + BD, ContextData); +} + LocationContextManager & AnalysisDeclContext::getLocationContextManager() { assert(Manager && "Cannot create LocationContexts without an AnalysisDeclContextManager!"); @@ -234,7 +242,7 @@ void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { } void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAnalysisDeclContext(), getParent(), BD); + Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData); } //===----------------------------------------------------------------------===// @@ -283,6 +291,24 @@ LocationContextManager::getScope(AnalysisDeclContext *ctx, return getLocationContext(ctx, parent, s); } +const BlockInvocationContext * +LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx, + const LocationContext *parent, + const BlockDecl *BD, + const void *ContextData) { + llvm::FoldingSetNodeID ID; + BlockInvocationContext::Profile(ID, ctx, parent, BD, ContextData); + void *InsertPos; + BlockInvocationContext *L = + cast_or_null(Contexts.FindNodeOrInsertPos(ID, + InsertPos)); + if (!L) { + L = new BlockInvocationContext(ctx, parent, BD, ContextData); + Contexts.InsertNode(L, InsertPos); + } + return L; +} + //===----------------------------------------------------------------------===// // LocationContext methods. //===----------------------------------------------------------------------===// @@ -297,19 +323,6 @@ const StackFrameContext *LocationContext::getCurrentStackFrame() const { return NULL; } -const StackFrameContext * -LocationContext::getStackFrameForDeclContext(const DeclContext *DC) const { - const LocationContext *LC = this; - while (LC) { - if (const StackFrameContext *SFC = dyn_cast(LC)) { - if (cast(SFC->getDecl()) == DC) - return SFC; - } - LC = LC->getParent(); - } - return NULL; -} - bool LocationContext::isParentOf(const LocationContext *LC) const { do { const LocationContext *Parent = LC->getParent(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 5604a33ae9..cab812ffdc 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -103,7 +103,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); - const LocationContext *callerCtx = calleeCtx->getParent(); + + // The parent context might not be a stack frame, so make sure we + // look up the first enclosing stack frame. + const StackFrameContext *callerCtx = + calleeCtx->getParent()->getCurrentStackFrame(); + const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. @@ -199,8 +204,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { } // Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); +bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const CFG *CalleeCFG = CalleeADC->getCFG(); // It is possible that the CFG cannot be constructed. @@ -212,7 +217,7 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { == AMgr.InlineMaxStackDepth) return false; - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) return false; if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) @@ -231,10 +236,7 @@ static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { if (const PointerType *PT = callee->getAs()) FT = dyn_cast(PT->getPointeeType()); else if (const BlockPointerType *BT = callee->getAs()) { - // FIXME: inline blocks. - // FT = dyn_cast(BT->getPointeeType()); - (void) BT; - return false; + FT = dyn_cast(BT->getPointeeType()); } // If we have no prototype, assume the function is okay. if (!FT) @@ -250,41 +252,64 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, if (!shouldInlineCallExpr(CE, this)) return false; + const StackFrameContext *CallerSFC = + Pred->getLocationContext()->getCurrentStackFrame(); + ProgramStateRef state = Pred->getState(); const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = - state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); - if (!FD || !FD->hasBody(FD)) - return false; + SVal CalleeVal = state->getSVal(Callee, Pred->getLocationContext()); + const Decl *D = 0; + const LocationContext *ParentOfCallee = 0; - switch (CE->getStmtClass()) { - default: - // FIXME: Handle C++. - break; - case Stmt::CallExprClass: { - if (!shouldInlineDecl(FD, Pred)) - return false; - - // Construct a new stack frame for the callee. - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); - const StackFrameContext *CallerSFC = - Pred->getLocationContext()->getCurrentStackFrame(); - const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(CallerSFC, CE, - currentBuilderContext->getBlock(), - currentStmtIdx); - - CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); - bool isNew; - if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { - N->addPredecessor(Pred, G); - if (isNew) - Engine.getWorkList()->enqueue(N); + if (const FunctionDecl *FD = CalleeVal.getAsFunctionDecl()) { + if (!FD->hasBody(FD)) + return false; + + switch (CE->getStmtClass()) { + default: + // FIXME: Handle C++. + break; + case Stmt::CallExprClass: { + D = FD; + break; + } - return true; } + } else if (const BlockDataRegion *BR = + dyn_cast_or_null(CalleeVal.getAsRegion())) { + assert(CE->getStmtClass() == Stmt::CallExprClass); + const BlockDecl *BD = BR->getDecl(); + D = BD; + AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(BD); + ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + BD, + BR); + } else { + // This is case we don't handle yet. + return false; } - return false; + + if (!D || !shouldInlineDecl(D, Pred)) + return false; + + if (!ParentOfCallee) + ParentOfCallee = CallerSFC; + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(ParentOfCallee, CE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } + return true; } static bool isPointerToConst(const ParmVarDecl *ParamDecl) { diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index e7c57ede88..dc309a0e42 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -643,6 +643,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ return getSubRegion(Str, getGlobalsRegion()); } +/// Look through a chain of LocationContexts to either find the +/// StackFrameContext that matches a DeclContext, or find a VarRegion +/// for a variable captured by a block. +static llvm::PointerUnion +getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD) { + while (LC) { + if (const StackFrameContext *SFC = dyn_cast(LC)) { + if (cast(SFC->getDecl()) == DC) + return SFC; + } + if (const BlockInvocationContext *BC = + dyn_cast(LC)) { + const BlockDataRegion *BR = + static_cast(BC->getContextData()); + // FIXME: This can be made more efficient. + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); I != E; ++I) { + if (const VarRegion *VR = dyn_cast(I.getOriginalRegion())) + if (VR->getDecl() == VD) + return cast(I.getCapturedRegion()); + } + } + + LC = LC->getParent(); + } + return (const StackFrameContext*)0; +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; @@ -675,7 +706,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + llvm::PointerUnion V = + getStackOrCaptureRegionForDeclContext(LC, DC, D); + + if (V.is()) + return V.get(); + + const StackFrameContext *STC = V.get(); if (!STC) sReg = getUnknownRegion(); diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b9cfa27808..a666de0c5c 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -568,6 +568,16 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { if (!scan(SR->getSuperRegion())) return false; + // Regions captured by a block are also implicitly reachable. + if (const BlockDataRegion *BDR = dyn_cast(R)) { + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + for ( ; I != E; ++I) { + if (!scan(I.getCapturedRegion())) + return false; + } + } + // Now look at the binding to this region (if any). if (!scan(state->getSValAsScalarOrLoc(R))) return false; diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index acc0ec4dcf..3a932bc7e2 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -394,6 +394,12 @@ public: // Part of public interface to class. const LocationContext *callerCtx, const StackFrameContext *calleeCtx); + StoreRef enterStackFrame(ProgramStateRef state, + const FunctionDecl *FD, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx); + + //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// @@ -1944,8 +1950,18 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { } // If V is a region, then add it to the worklist. - if (const MemRegion *R = V.getAsRegion()) + if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); + + // All regions captured by a block are also live. + if (const BlockDataRegion *BR = dyn_cast(R)) { + BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); + for ( ; I != E; ++I) + AddToWorkList(I.getCapturedRegion()); + } + } + // Update the set of live symbols. for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); @@ -1964,21 +1980,6 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) { // should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast(R)) SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr()) - AddToWorkList(*RI); - } - - // No possible data bindings on a BlockDataRegion. - return; - } } // Visit the data binding for K. @@ -2045,12 +2046,37 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, return StoreRef(B.getRootWithoutRetain(), *this); } +StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) +{ + const Decl *D = calleeCtx->getDecl(); + if (const FunctionDecl *FD = dyn_cast(D)) + return enterStackFrame(state, FD, callerCtx, calleeCtx); + + // FIXME: when we handle more cases, this will need to be expanded. + + const BlockDecl *BD = cast(D); + BlockDecl::param_const_iterator PI = BD->param_begin(), + PE = BD->param_end(); + StoreRef store = StoreRef(state->getStore(), *this); + const CallExpr *CE = cast(calleeCtx->getCallSite()); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + SVal ArgVal = state->getSVal(*AI, callerCtx); + store = Bind(store.getStore(), + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); + } + + return store; +} StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const FunctionDecl *FD, const LocationContext *callerCtx, const StackFrameContext *calleeCtx) { - FunctionDecl const *FD = cast(calleeCtx->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); StoreRef store = StoreRef(state->getStore(), *this); diff --git a/test/Analysis/blocks.m b/test/Analysis/blocks.m index 7a604ddb4d..ff376d17a1 100644 --- a/test/Analysis/blocks.m +++ b/test/Analysis/blocks.m @@ -79,9 +79,7 @@ void test2() { void test2_b() { static int y = 0; __block int x; - // This is also a bug, but should be found not by checking the value - // 'x' is bound at block creation. - ^{ y = x + 1; }(); // no-warning + ^{ y = x + 1; }(); // expected-warning {{left operand of '+' is a garbage value}} } void test2_c() { diff --git a/test/Analysis/inline-plist.c b/test/Analysis/inline-plist.c index 0510e8f21d..cd57c7b310 100644 --- a/test/Analysis/inline-plist.c +++ b/test/Analysis/inline-plist.c @@ -1,4 +1,4 @@ -// RUN: %clang --analyze %s -Xclang -analyzer-ipa=inlining -o %t +// RUN: %clang --analyze %s -Xclang -analyzer-ipa=inlining -fblocks -o %t // RUN: FileCheck -input-file %t %s // @@ -37,6 +37,32 @@ void bar(int *p) { triggers_bug(p); } +// ========================================================================== // +// Test inlining of blocks. +// ========================================================================== // + +void test_block__capture_null() { + int *p = 0; + ^(){ *p = 1; }(); +} + +void test_block_ret() { + int *p = ^(){ int *q = 0; return q; }(); + *p = 1; +} + +void test_block_blockvar() { + __block int *p; + ^(){ p = 0; }(); + *p = 1; +} + +void test_block_arg() { + int *p; + ^(int **q){ *q = 0; }(&p); + *p = 1; +} + // CHECK: // CHECK: // CHECK: @@ -630,6 +656,528 @@ void bar(int *p) { // CHECK: file0 // CHECK: // CHECK: +// CHECK: +// CHECK: path +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line45 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line45 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line45 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth0 +// CHECK: extended_message +// CHECK: Variable 'p' initialized to a null pointer value +// CHECK: message +// CHECK: Variable 'p' initialized to a null pointer value +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line45 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line45 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col18 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth0 +// CHECK: extended_message +// CHECK: Calling anonymous block +// CHECK: message +// CHECK: Calling anonymous block +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: depth1 +// CHECK: extended_message +// CHECK: Entered call from 'test_block__capture_null' +// CHECK: message +// CHECK: Entered call from 'test_block__capture_null' +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line46 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col9 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line46 +// CHECK: col9 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth1 +// CHECK: extended_message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: +// CHECK: +// CHECK: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK: categoryLogic error +// CHECK: typeDereference of null pointer +// CHECK: location +// CHECK: +// CHECK: line46 +// CHECK: col8 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: path +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col12 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col12 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col12 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line50 +// CHECK: col41 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line51 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line51 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line51 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line51 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line51 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth0 +// CHECK: extended_message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: +// CHECK: +// CHECK: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK: categoryLogic error +// CHECK: typeDereference of null pointer +// CHECK: issue_context_kindfunction +// CHECK: issue_contexttest_block_ret +// CHECK: location +// CHECK: +// CHECK: line51 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: path +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line55 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line55 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line56 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line56 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line56 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line56 +// CHECK: col17 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line57 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line57 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line57 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line57 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line57 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth0 +// CHECK: extended_message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: +// CHECK: +// CHECK: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK: categoryLogic error +// CHECK: typeDereference of null pointer +// CHECK: issue_context_kindfunction +// CHECK: issue_contexttest_block_blockvar +// CHECK: location +// CHECK: +// CHECK: line57 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: path +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line61 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line61 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line62 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line62 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindcontrol +// CHECK: edges +// CHECK: +// CHECK: +// CHECK: start +// CHECK: +// CHECK: +// CHECK: line62 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line62 +// CHECK: col27 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: end +// CHECK: +// CHECK: +// CHECK: line63 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line63 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: +// CHECK: kindevent +// CHECK: location +// CHECK: +// CHECK: line63 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: ranges +// CHECK: +// CHECK: +// CHECK: +// CHECK: line63 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: line63 +// CHECK: col4 +// CHECK: file0 +// CHECK: +// CHECK: +// CHECK: +// CHECK: depth0 +// CHECK: extended_message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: message +// CHECK: Dereference of null pointer (loaded from variable 'p') +// CHECK: +// CHECK: +// CHECK: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK: categoryLogic error +// CHECK: typeDereference of null pointer +// CHECK: issue_context_kindfunction +// CHECK: issue_contexttest_block_arg +// CHECK: location +// CHECK: +// CHECK: line63 +// CHECK: col3 +// CHECK: file0 +// CHECK: +// CHECK: // CHECK: // CHECK: // CHECK: diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 840f03cee1..cda60b1420 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -1452,8 +1452,7 @@ void test_blocks_1_indirect_release_via_call(void) { } void test_blocks_1_indirect_retain_via_call(void) { - // Eventually this should be reported as a leak. - NSNumber *number = [[NSNumber alloc] initWithInt:5]; // no-warning + NSNumber *number = [[NSNumber alloc] initWithInt:5]; // expected-warning {{leak}} ^(NSObject *o){ [o retain]; }(number); } -- 2.40.0