static analyzer: add inlining support for directly called blocks.

llvm-svn: 157833
This commit is contained in:
Ted Kremenek 2012-06-01 20:04:04 +00:00
parent 0bc272cf9a
commit c3da376fbc
11 changed files with 758 additions and 87 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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() {}

View File

@ -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<ScopeContext, Stmt>(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<BlockInvocationContext>(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<StackFrameContext>(LC)) {
if (cast<DeclContext>(SFC->getDecl()) == DC)
return SFC;
}
LC = LC->getParent();
}
return NULL;
}
bool LocationContext::isParentOf(const LocationContext *LC) const {
do {
const LocationContext *Parent = LC->getParent();

View File

@ -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<PointerType>())
FT = dyn_cast<FunctionProtoType>(PT->getPointeeType());
else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) {
// FIXME: inline blocks.
// FT = dyn_cast<FunctionProtoType>(BT->getPointeeType());
(void) BT;
return false;
FT = dyn_cast<FunctionProtoType>(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))
SVal CalleeVal = state->getSVal(Callee, Pred->getLocationContext());
const Decl *D = 0;
const LocationContext *ParentOfCallee = 0;
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;
}
}
} else if (const BlockDataRegion *BR =
dyn_cast_or_null<BlockDataRegion>(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;
}
if (!D || !shouldInlineDecl(D, Pred))
return false;
switch (CE->getStmtClass()) {
default:
// FIXME: Handle C++.
break;
case Stmt::CallExprClass: {
if (!shouldInlineDecl(FD, Pred))
return false;
if (!ParentOfCallee)
ParentOfCallee = CallerSFC;
// 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);
}
return true;
}
// 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 false;
return true;
}
static bool isPointerToConst(const ParmVarDecl *ParamDecl) {

View File

@ -643,6 +643,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
return getSubRegion<ObjCStringRegion>(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<const StackFrameContext *, const VarRegion *>
getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
const DeclContext *DC,
const VarDecl *VD) {
while (LC) {
if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) {
if (cast<DeclContext>(SFC->getDecl()) == DC)
return SFC;
}
if (const BlockInvocationContext *BC =
dyn_cast<BlockInvocationContext>(LC)) {
const BlockDataRegion *BR =
static_cast<const BlockDataRegion*>(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<VarRegion>(I.getOriginalRegion()))
if (VR->getDecl() == VD)
return cast<VarRegion>(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<const StackFrameContext *, const VarRegion *> V =
getStackOrCaptureRegionForDeclContext(LC, DC, D);
if (V.is<const VarRegion*>())
return V.get<const VarRegion*>();
const StackFrameContext *STC = V.get<const StackFrameContext*>();
if (!STC)
sReg = getUnknownRegion();

View File

@ -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<BlockDataRegion>(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;

View File

@ -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<BlockDataRegion>(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<SymbolicRegion>(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<BlockDataRegion>(R)) {
for (BlockDataRegion::referenced_vars_iterator
RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end();
RI != RE; ++RI) {
if ((*RI)->getDecl()->getAttr<BlocksAttr>())
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)
{
FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl());
const Decl *D = calleeCtx->getDecl();
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
return enterStackFrame(state, FD, callerCtx, calleeCtx);
// FIXME: when we handle more cases, this will need to be expanded.
const BlockDecl *BD = cast<BlockDecl>(D);
BlockDecl::param_const_iterator PI = BD->param_begin(),
PE = BD->param_end();
StoreRef store = StoreRef(state->getStore(), *this);
const CallExpr *CE = cast<CallExpr>(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::param_const_iterator PI = FD->param_begin(),
PE = FD->param_end();
StoreRef store = StoreRef(state->getStore(), *this);

View File

@ -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() {

View File

@ -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
// <rdar://problem/10967815>
@ -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: <?xml version="1.0" encoding="UTF-8"?>
// CHECK: <plist version="1.0">
// CHECK: <dict>
@ -630,6 +656,528 @@ void bar(int *p) {
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: <key>message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>45</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>18</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Calling anonymous block</string>
// CHECK: <key>message</key>
// CHECK: <string>Calling anonymous block</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>depth</key><integer>1</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Entered call from &apos;test_block__capture_null&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Entered call from &apos;test_block__capture_null&apos;</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>9</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>1</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>category</key><string>Logic error</string>
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>46</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>12</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>12</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>12</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>50</integer>
// CHECK: <key>col</key><integer>41</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>category</key><string>Logic error</string>
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_block_ret</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>51</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>55</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>17</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>category</key><string>Logic error</string>
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_block_blockvar</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>57</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>61</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>61</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>62</integer>
// CHECK: <key>col</key><integer>27</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>4</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>depth</key><integer>0</integer>
// CHECK: <key>extended_message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>message</key>
// CHECK: <string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>description</key><string>Dereference of null pointer (loaded from variable &apos;p&apos;)</string>
// CHECK: <key>category</key><string>Logic error</string>
// CHECK: <key>type</key><string>Dereference of null pointer</string>
// CHECK: <key>issue_context_kind</key><string>function</string>
// CHECK: <key>issue_context</key><string>test_block_arg</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>63</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </plist>

View File

@ -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);
}