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
diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h
index 4e2a544..46b4e93 100644
--- a/include/clang/Analysis/AnalysisContext.h
+++ b/include/clang/Analysis/AnalysisContext.h
@@ -38,6 +38,7 @@
 class ImplicitParamDecl;
 class LocationContextManager;
 class StackFrameContext;
+class BlockInvocationContext;
 class AnalysisDeclContextManager;
 class LocationContext;
 
@@ -162,6 +163,11 @@
                                          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 @@
   }
 
   const StackFrameContext *getCurrentStackFrame() const;
-  const StackFrameContext *
-    getStackFrameForDeclContext(const DeclContext *DC) const;
 
   virtual void Profile(llvm::FoldingSetNodeID &ID) = 0;
 
@@ -307,27 +311,32 @@
 };
 
 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 @@
   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 @@
     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 73f39b1..14087c9 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -494,7 +494,7 @@
                     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 8431b0e..5d27f86 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -572,7 +572,7 @@
 
   RegionSetTy RegionRoots;
   
-  const LocationContext *LCtx;
+  const StackFrameContext *LCtx;
   const Stmt *Loc;
   SymbolManager& SymMgr;
   StoreRef reapedStore;
@@ -586,7 +586,8 @@
   /// 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 6243733..b618569 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -204,6 +204,14 @@
   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 BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) {
-  Profile(ID, getAnalysisDeclContext(), getParent(), BD);
+  Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData);
 }
 
 //===----------------------------------------------------------------------===//
@@ -283,6 +291,24 @@
   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 @@
   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();
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 5604a33..cab812f 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -103,7 +103,12 @@
 
   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 @@
 }
 
 // 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 @@
         == 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 @@
   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 @@
   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) {
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index e7c57ed..dc309a0 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -643,6 +643,37 @@
   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 @@
     // 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();
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index b9cfa27..a666de0 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -568,6 +568,16 @@
     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;
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index acc0ec4..3a932bc 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -394,6 +394,12 @@
                            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 @@
   }
 
   // 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 @@
     // 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 @@
   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);
diff --git a/test/Analysis/blocks.m b/test/Analysis/blocks.m
index 7a604dd..ff376d1 100644
--- a/test/Analysis/blocks.m
+++ b/test/Analysis/blocks.m
@@ -79,9 +79,7 @@
 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 0510e8f..cd57c7b 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
 
 // <rdar://problem/10967815>
@@ -37,6 +37,32 @@
     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 @@
 // 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>
diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m
index 840f03c..cda60b1 100644
--- a/test/Analysis/retain-release.m
+++ b/test/Analysis/retain-release.m
@@ -1452,8 +1452,7 @@
 }
 
 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);
 }