diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp index 9e6d79bb7dcc9f..1ccf4c6104a652 100644 --- a/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -183,18 +183,12 @@ EnvironmentManager::removeDeadBindings(Environment Env, F.getTreeFactory()); // Iterate over the block-expr bindings. - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + for (Environment::iterator I = Env.begin(), E = Env.end(); + I != E; ++I) { const EnvironmentEntry &BlkExpr = I.getKey(); const SVal &X = I.getData(); - const bool IsBlkExprLive = - SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext()); - - assert((isa(BlkExpr.getStmt()) || !IsBlkExprLive) && - "Only Exprs can be live, LivenessAnalysis argues about the liveness " - "of *values*!"); - - if (IsBlkExprLive) { + if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); diff --git a/clang/test/Analysis/live-stmts.mm b/clang/test/Analysis/live-stmts.mm new file mode 100644 index 00000000000000..a6ddd03ca5d851 --- /dev/null +++ b/clang/test/Analysis/live-stmts.mm @@ -0,0 +1,101 @@ +// RUN: %clang_analyze_cc1 -w -fblocks %s \ +// RUN: -analyzer-checker=debug.DumpLiveStmts \ +// RUN: 2>&1 | FileCheck %s + +@interface Item +// ... +@end + +@interface Collection +// ... +@end + +typedef void (^Blk)(); + +struct RAII { + Blk blk; + +public: + RAII(Blk blk): blk(blk) {} + +// CHECK: [ B0 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B1 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B2 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: + + ~RAII() { blk(); } + +// CHECK-NEXT: [ B0 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B1 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B2 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +}; + +void foo(Collection *coll) { + RAII raii(^{}); + for (Item *item in coll) {} +} +// CHECK-NEXT: [ B0 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B1 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B2 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-NEXT: DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *' +// CHECK-EMPTY: +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *' +// CHECK-EMPTY: +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B3 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-NEXT: DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *' +// CHECK-EMPTY: +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *' +// CHECK-EMPTY: +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B4 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-NEXT: DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *' +// CHECK-EMPTY: +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Collection *' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Collection *' lvalue ParmVar {{.*}} 'coll' 'Collection *' +// CHECK-EMPTY: +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B5 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-NEXT: DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} item 'Item *' +// CHECK-EMPTY: +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B0 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +// CHECK-NEXT: [ B1 (live statements at block exit) ] +// CHECK-EMPTY: +// CHECK-EMPTY: +