r254107 - [analyzer] Include block capture copy expressions in the CFG.

Devin Coughlin via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 25 14:35:37 PST 2015


Author: dcoughlin
Date: Wed Nov 25 16:35:37 2015
New Revision: 254107

URL: http://llvm.org/viewvc/llvm-project?rev=254107&view=rev
Log:
[analyzer] Include block capture copy expressions in the CFG.

This prevents spurious dead store warnings when a C++ lambda is casted to a block.

I've also added several tests documenting our still-incomplete support for lambda-to-block
casts.

rdar://problem/22236293

Added:
    cfe/trunk/test/Analysis/blocks.mm
Modified:
    cfe/trunk/lib/Analysis/CFG.cpp
    cfe/trunk/test/Analysis/lambdas.mm

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=254107&r1=254106&r2=254107&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Wed Nov 25 16:35:37 2015
@@ -460,6 +460,7 @@ private:
   CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
   CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I);
   CFGBlock *VisitLabelStmt(LabelStmt *L);
+  CFGBlock *VisitBlockExpr(BlockExpr *E, AddStmtChoice asc);
   CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc);
   CFGBlock *VisitLogicalOperator(BinaryOperator *B);
   std::pair<CFGBlock *, CFGBlock *> VisitLogicalOperator(BinaryOperator *B,
@@ -1453,7 +1454,7 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, Ad
       return VisitBinaryOperator(cast<BinaryOperator>(S), asc);
 
     case Stmt::BlockExprClass:
-      return VisitNoRecurse(cast<Expr>(S), asc);
+      return VisitBlockExpr(cast<BlockExpr>(S), asc);
 
     case Stmt::BreakStmtClass:
       return VisitBreakStmt(cast<BreakStmt>(S));
@@ -2333,6 +2334,18 @@ CFGBlock *CFGBuilder::VisitLabelStmt(Lab
   return LabelBlock;
 }
 
+CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, AddStmtChoice asc) {
+  CFGBlock *LastBlock = VisitNoRecurse(E, asc);
+  for (const BlockDecl::Capture &CI : E->getBlockDecl()->captures()) {
+    if (Expr *CopyExpr = CI.getCopyExpr()) {
+      CFGBlock *Tmp = Visit(CopyExpr);
+      if (Tmp)
+        LastBlock = Tmp;
+    }
+  }
+  return LastBlock;
+}
+
 CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) {
   CFGBlock *LastBlock = VisitNoRecurse(E, asc);
   for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(),

Added: cfe/trunk/test/Analysis/blocks.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/blocks.mm?rev=254107&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/blocks.mm (added)
+++ cfe/trunk/test/Analysis/blocks.mm Wed Nov 25 16:35:37 2015
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core -fblocks -analyzer-opt-analyze-nested-blocks -verify -x objective-c++ %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -fblocks -analyzer-opt-analyze-nested-blocks  %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
+
+// expected-no-diagnostics
+
+void testBlockWithoutCopyExpression(int i) {
+  // Captures i, with no copy expression.
+  (void)(^void() {
+    (void)i;
+  });
+}
+
+// CHECK-LABEL:void testBlockWithoutCopyExpression(int i)
+// CHECK-NEXT: [B2 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B1
+
+// CHECK: [B1]
+// CHECK-NEXT:   1: ^{ }
+// CHECK-NEXT:   2: (void)([B1.1]) (CStyleCastExpr, ToVoid, void)
+// CHECK-NEXT:   Preds (1): B2
+// CHECK-NEXT:   Succs (1): B0
+
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1
+
+struct StructWithCopyConstructor {
+  StructWithCopyConstructor(int i);
+  StructWithCopyConstructor(const StructWithCopyConstructor &s);
+};
+void testBlockWithCopyExpression(StructWithCopyConstructor s) {
+  // Captures s, with a copy expression calling the copy constructor for StructWithCopyConstructor.
+  (void)(^void() {
+    (void)s;
+  });
+}
+
+// CHECK-LABEL:void testBlockWithCopyExpression(StructWithCopyConstructor s)
+// CHECK-NEXT: [B2 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B1
+
+// CHECK: [B1]
+// CHECK-NEXT:   1: s
+// CHECK-NEXT:   2: [B1.1] (CXXConstructExpr, const struct StructWithCopyConstructor)
+// CHECK-NEXT:   3: ^{ }
+// CHECK-NEXT:   4: (void)([B1.3]) (CStyleCastExpr, ToVoid, void)
+// CHECK-NEXT:   Preds (1): B2
+// CHECK-NEXT:   Succs (1): B0
+
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1
+
+void testBlockWithCaptureByReference() {
+  __block StructWithCopyConstructor s(5);
+  // Captures s by reference, so no copy expression.
+  (void)(^void() {
+    (void)s;
+  });
+}
+
+// CHECK-LABEL:void testBlockWithCaptureByReference()
+// CHECK-NEXT: [B2 (ENTRY)]
+// CHECK-NEXT:   Succs (1): B1
+
+// CHECK: [B1]
+// CHECK-NEXT:   1: 5
+// CHECK-NEXT:   2: [B1.1] (CXXConstructExpr, struct StructWithCopyConstructor)
+// CHECK-NEXT:   3: StructWithCopyConstructor s(5) __attribute__((blocks("byref")));
+// CHECK-NEXT:   4: ^{ }
+// CHECK-NEXT:   5: (void)([B1.4]) (CStyleCastExpr, ToVoid, void)
+// CHECK-NEXT:   Preds (1): B2
+// CHECK-NEXT:   Succs (1): B0
+
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT:   Preds (1): B1

Modified: cfe/trunk/test/Analysis/lambdas.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lambdas.mm?rev=254107&r1=254106&r2=254107&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/lambdas.mm (original)
+++ cfe/trunk/test/Analysis/lambdas.mm Wed Nov 25 16:35:37 2015
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-objc-root-class -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fblocks -Wno-objc-root-class -analyze -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
 
 int clang_analyzer_eval(int);
 
@@ -44,3 +44,43 @@ int clang_analyzer_eval(int);
 }
 
 @end
+
+int getValue();
+void useValue(int v);
+
+void castToBlockNoDeadStore() {
+  int v = getValue(); // no-warning
+
+  (void)(void(^)())[v]() { // This capture should count as a use, so no dead store warning above.
+  };
+}
+
+void takesBlock(void(^block)());
+
+void passToFunctionTakingBlockNoDeadStore() {
+  int v = 7; // no-warning
+  int x = 8; // no-warning
+  takesBlock([&v, x]() {
+    (void)v;
+  });
+}
+
+void castToBlockAndInline() {
+  int result = ((int(^)(int))[](int p) {
+    return p;
+  })(7);
+
+  // FIXME: This should be TRUE. We're not handling lambda to block conversions
+  // properly in ExprEngine::VisitBlockExpr.
+  clang_analyzer_eval(result == 7); // expected-warning{{UNKNOWN}}
+}
+
+void castLambdaInLocalBlock() {
+  // FIXME: This results in a spurious
+  // "Address of stack-allocated block declared on line XX returned to caller" warning
+  // because we're not handling lambda to block conversions properly in ExprEngine.
+  auto lambda = []{ }; // expected-warning {{Address of stack-allocated block declared on line}}
+
+  void(^block)() = lambda;
+  (void)block;
+}




More information about the cfe-commits mailing list