r247426 - [Static Analyzer] Lambda support.

Gabor Horvath via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 09:55:01 PDT 2015


Author: xazax
Date: Fri Sep 11 11:55:01 2015
New Revision: 247426

URL: http://llvm.org/viewvc/llvm-project?rev=247426&view=rev
Log:
[Static Analyzer] Lambda support.

Differential Revision: http://reviews.llvm.org/D12652


Added:
    cfe/trunk/test/Analysis/lambda-notes.cpp
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
    cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/MemRegion.cpp
    cfe/trunk/test/Analysis/dead-stores.cpp
    cfe/trunk/test/Analysis/lambdas.cpp
    cfe/trunk/test/Analysis/temporaries.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h Fri Sep 11 11:55:01 2015
@@ -256,6 +256,9 @@ private:
   /// \sa getMaxNodesPerTopLevelFunction
   Optional<unsigned> MaxNodesPerTopLevelFunction;
 
+  /// \sa shouldInlineLambdas
+  Optional<bool> InlineLambdas;
+
   /// A helper function that retrieves option for a given full-qualified
   /// checker name.
   /// Options for checkers can be specified via 'analyzer-config' command-line
@@ -509,6 +512,10 @@ public:
   /// This is controlled by the 'max-nodes' config option.
   unsigned getMaxNodesPerTopLevelFunction();
 
+  /// Returns true if lambdas should be inlined. Otherwise a sink node will be
+  /// generated each time a LambdaExpr is visited.
+  bool shouldInlineLambdas();
+
 public:
   AnalyzerOptions() :
     AnalysisStoreOpt(RegionStoreModel),

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Fri Sep 11 11:55:01 2015
@@ -341,6 +341,10 @@ public:
   void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, 
                       ExplodedNodeSet &Dst);
 
+  /// VisitLambdaExpr - Transfer function logic for LambdaExprs.
+  void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, 
+                       ExplodedNodeSet &Dst);
+
   /// VisitBinaryOperator - Transfer function logic for binary operators.
   void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, 
                            ExplodedNodeSet &Dst);

Modified: cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp Fri Sep 11 11:55:01 2015
@@ -325,3 +325,7 @@ bool AnalyzerOptions::shouldPrunePaths()
 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
   return getBooleanOption("cfg-conditional-static-initializers", true);
 }
+
+bool AnalyzerOptions::shouldInlineLambdas() {
+  return getBooleanOption("inline-lambdas", /*Default=*/true);
+}

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Fri Sep 11 11:55:01 2015
@@ -769,7 +769,6 @@ void ExprEngine::Visit(const Stmt *S, Ex
     case Stmt::SEHTryStmtClass:
     case Stmt::SEHExceptStmtClass:
     case Stmt::SEHLeaveStmtClass:
-    case Stmt::LambdaExprClass:
     case Stmt::SEHFinallyStmtClass: {
       const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
       Engine.addAbortedBlock(node, currBldrCtx->getBlock());
@@ -1013,6 +1012,17 @@ void ExprEngine::Visit(const Stmt *S, Ex
       Bldr.addNodes(Dst);
       break;
 
+    case Stmt::LambdaExprClass:
+      if (AMgr.options.shouldInlineLambdas()) {
+        Bldr.takeNodes(Pred);
+        VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst);
+        Bldr.addNodes(Dst);
+      } else {
+        const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
+        Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+      }
+      break;
+
     case Stmt::BinaryOperatorClass: {
       const BinaryOperator* B = cast<BinaryOperator>(S);
       if (B->isLogicalOp()) {
@@ -1853,11 +1863,35 @@ void ExprEngine::VisitCommonDeclRefExpr(
     // C permits "extern void v", and if you cast the address to a valid type,
     // you can even do things with it. We simply pretend
     assert(Ex->isGLValue() || VD->getType()->isVoidType());
-    SVal V = state->getLValue(VD, Pred->getLocationContext());
+    const LocationContext *LocCtxt = Pred->getLocationContext();
+    const Decl *D = LocCtxt->getDecl();
+    const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr;
+    const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
+    SVal V;
+    bool CaptureByReference = false;
+    if (AMgr.options.shouldInlineLambdas() && DeclRefEx &&
+        DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
+        MD->getParent()->isLambda()) {
+      // Lookup the field of the lambda.
+      const CXXRecordDecl *CXXRec = MD->getParent();
+      llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+      FieldDecl *LambdaThisCaptureField;
+      CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
+      const FieldDecl *FD = LambdaCaptureFields[VD];
+      Loc CXXThis =
+          svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame());
+      SVal CXXThisVal = state->getSVal(CXXThis);
+      V = state->getLValue(FD, CXXThisVal);
+      if (FD->getType()->isReferenceType() &&
+          !VD->getType()->isReferenceType())
+        CaptureByReference = true;
+    } else {
+      V = state->getLValue(VD, LocCtxt);
+    }
 
     // For references, the 'lvalue' is the pointer address stored in the
     // reference region.
-    if (VD->getType()->isReferenceType()) {
+    if (VD->getType()->isReferenceType() || CaptureByReference) {
       if (const MemRegion *R = V.getAsRegion())
         V = state->getSVal(R);
       else

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Fri Sep 11 11:55:01 2015
@@ -513,3 +513,41 @@ void ExprEngine::VisitCXXThisExpr(const
   SVal V = state->getSVal(loc::MemRegionVal(R));
   Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V));
 }
+
+void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
+                                 ExplodedNodeSet &Dst) {
+  const LocationContext *LocCtxt = Pred->getLocationContext();
+
+  // Get the region of the lambda itself.
+  const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion(
+      LE, LocCtxt);
+  SVal V = loc::MemRegionVal(R);
+  
+  ProgramStateRef State = Pred->getState();
+  
+  // If we created a new MemRegion for the lambda, we should explicitly bind
+  // the captures.
+  CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
+  for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
+                                               e = LE->capture_init_end();
+       i != e; ++i, ++CurField) {
+    SVal Field = State->getLValue(*CurField, V);
+    SVal InitExpr = State->getSVal(*i, LocCtxt);
+    State = State->bindLoc(Field, InitExpr);
+  }
+
+  // Decay the Loc into an RValue, because there might be a
+  // MaterializeTemporaryExpr node above this one which expects the bound value
+  // to be an RValue.
+  SVal LambdaRVal = State->getSVal(R);
+
+  ExplodedNodeSet Tmp;
+  StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+  // FIXME: is this the right program point kind?
+  Bldr.generateNode(LE, Pred,
+                    State->BindExpr(LE, LocCtxt, LambdaRVal),
+                    nullptr, ProgramPoint::PostLValueKind);
+
+  // FIXME: Move all post/pre visits to ::Visit().
+  getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this);
+}

Modified: cfe/trunk/lib/StaticAnalyzer/Core/MemRegion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/MemRegion.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/MemRegion.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/MemRegion.cpp Fri Sep 11 11:55:01 2015
@@ -1013,10 +1013,21 @@ MemRegionManager::getCXXBaseObjectRegion
 const CXXThisRegion*
 MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
                                    const LocationContext *LC) {
-  const StackFrameContext *STC = LC->getCurrentStackFrame();
-  assert(STC);
   const PointerType *PT = thisPointerTy->getAs<PointerType>();
   assert(PT);
+  // Inside the body of the operator() of a lambda a this expr might refer to an
+  // object in one of the parent location contexts.
+  const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl());
+  // FIXME: when operator() of lambda is analyzed as a top level function and
+  // 'this' refers to a this to the enclosing scope, there is no right region to
+  // return.
+  while (!LC->inTopFrame() &&
+         PT != D->getThisType(getContext())->getAs<PointerType>()) {
+    LC = LC->getParent();
+    D = dyn_cast<CXXMethodDecl>(LC->getDecl());
+  }
+  const StackFrameContext *STC = LC->getCurrentStackFrame();
+  assert(STC);
   return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC));
 }
 

Modified: cfe/trunk/test/Analysis/dead-stores.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/dead-stores.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/dead-stores.cpp (original)
+++ cfe/trunk/test/Analysis/dead-stores.cpp Fri Sep 11 11:55:01 2015
@@ -174,3 +174,17 @@ int radar_13213575() {
   return radar13213575_testit<true>(5) + radar13213575_testit<false>(3);
 }
 
+//===----------------------------------------------------------------------===//
+// Dead store checking involving lambdas.
+//===----------------------------------------------------------------------===//
+
+int basicLambda(int i, int j) {
+  i = 5; // no warning
+  j = 6; // no warning
+  [i] { (void)i; }();
+  [&j] { (void)j; }();
+  i = 2;
+  j = 3;
+  return i + j;
+}
+

Added: cfe/trunk/test/Analysis/lambda-notes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lambda-notes.cpp?rev=247426&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/lambda-notes.cpp (added)
+++ cfe/trunk/test/Analysis/lambda-notes.cpp Fri Sep 11 11:55:01 2015
@@ -0,0 +1,204 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core -analyzer-config inline-lambdas=true -analyzer-output plist -verify %s -o %t
+// RUN: FileCheck --input-file=%t %s
+
+
+// Diagnostic inside a lambda
+
+void diagnosticFromLambda() {
+  int i = 0;
+  [=] {
+    int p = 5/i; // expected-warning{{Division by zero}}
+    (void)p;
+  }();
+}
+
+// CHECK:  <array>
+// 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>8</integer>
+// CHECK:            <key>col</key><integer>3</integer>
+// CHECK:            <key>file</key><integer>0</integer>
+// CHECK:           </dict>
+// CHECK:           <dict>
+// CHECK:            <key>line</key><integer>8</integer>
+// CHECK:            <key>col</key><integer>5</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>9</integer>
+// CHECK:            <key>col</key><integer>3</integer>
+// CHECK:            <key>file</key><integer>0</integer>
+// CHECK:           </dict>
+// CHECK:           <dict>
+// CHECK:            <key>line</key><integer>9</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>9</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>9</integer>
+// CHECK:          <key>col</key><integer>3</integer>
+// CHECK:          <key>file</key><integer>0</integer>
+// CHECK:         </dict>
+// CHECK:         <dict>
+// CHECK:          <key>line</key><integer>12</integer>
+// CHECK:          <key>col</key><integer>5</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>The value 0 is assigned to field ''</string>
+// CHECK:      <key>message</key>
+// CHECK:      <string>The value 0 is assigned to field ''</string>
+// CHECK:     </dict>
+// CHECK:     <dict>
+// CHECK:      <key>kind</key><string>event</string>
+// CHECK:      <key>location</key>
+// CHECK:      <dict>
+// CHECK:       <key>line</key><integer>9</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>9</integer>
+// CHECK:          <key>col</key><integer>3</integer>
+// CHECK:          <key>file</key><integer>0</integer>
+// CHECK:         </dict>
+// CHECK:         <dict>
+// CHECK:          <key>line</key><integer>12</integer>
+// CHECK:          <key>col</key><integer>5</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 'operator()'</string>
+// CHECK:      <key>message</key>
+// CHECK:      <string>Calling 'operator()'</string>
+// CHECK:     </dict>
+// CHECK:     <dict>
+// CHECK:      <key>kind</key><string>event</string>
+// CHECK:      <key>location</key>
+// CHECK:      <dict>
+// CHECK:       <key>line</key><integer>9</integer>
+// CHECK:       <key>col</key><integer>5</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 'diagnosticFromLambda'</string>
+// CHECK:      <key>message</key>
+// CHECK:      <string>Entered call from 'diagnosticFromLambda'</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>9</integer>
+// CHECK:            <key>col</key><integer>5</integer>
+// CHECK:            <key>file</key><integer>0</integer>
+// CHECK:           </dict>
+// CHECK:           <dict>
+// CHECK:            <key>line</key><integer>9</integer>
+// CHECK:            <key>col</key><integer>5</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>10</integer>
+// CHECK:            <key>col</key><integer>14</integer>
+// CHECK:            <key>file</key><integer>0</integer>
+// CHECK:           </dict>
+// CHECK:           <dict>
+// CHECK:            <key>line</key><integer>10</integer>
+// CHECK:            <key>col</key><integer>14</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>10</integer>
+// CHECK:       <key>col</key><integer>14</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>10</integer>
+// CHECK:          <key>col</key><integer>13</integer>
+// CHECK:          <key>file</key><integer>0</integer>
+// CHECK:         </dict>
+// CHECK:         <dict>
+// CHECK:          <key>line</key><integer>10</integer>
+// CHECK:          <key>col</key><integer>15</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>Division by zero</string>
+// CHECK:      <key>message</key>
+// CHECK:      <string>Division by zero</string>
+// CHECK:     </dict>
+// CHECK:    </array>
+// CHECK:    <key>description</key><string>Division by zero</string>
+// CHECK:    <key>category</key><string>Logic error</string>
+// CHECK:    <key>type</key><string>Division by zero</string>
+// CHECK:    <key>check_name</key><string>core.DivideZero</string>
+// CHECK:   <key>issue_context_kind</key><string>C++ method</string>
+// CHECK:   <key>issue_context</key><string>operator()</string>
+// CHECK:   <key>issue_hash</key><string>1</string>
+// CHECK:   <key>location</key>
+// CHECK:   <dict>
+// CHECK:    <key>line</key><integer>10</integer>
+// CHECK:    <key>col</key><integer>14</integer>
+// CHECK:    <key>file</key><integer>0</integer>
+// CHECK:   </dict>
+// CHECK:   </dict>
+// CHECK:  </array>
+

Modified: cfe/trunk/test/Analysis/lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lambdas.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/lambdas.cpp (original)
+++ cfe/trunk/test/Analysis/lambdas.cpp Fri Sep 11 11:55:01 2015
@@ -1,9 +1,181 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=debug.DumpCFG %s > %t 2>&1
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s 
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(int);
+
 struct X { X(const X&); };
 void f(X x) { (void) [x]{}; }
 
+
+// Lambda semantics tests.
+
+void basicCapture() {
+  int i = 5;
+  [i]() mutable {
+    // clang_analyzer_eval does nothing in inlined functions.
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+    ++i;
+  }();
+  [&i] {
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+  }();
+  [&i] {
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+    i++;
+  }();
+  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
+}
+
+void deferredLambdaCall() {
+  int i = 5;
+  auto l1 = [i]() mutable {
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+    ++i;
+  };
+  auto l2 = [&i] {
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+  };
+  auto l3 = [&i] {
+    if (i != 5)
+      clang_analyzer_warnIfReached();
+    i++;
+  };
+  l1();
+  l2();
+  l3();
+  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
+}
+
+void multipleCaptures() {
+  int i = 5, j = 5;
+  [i, &j]() mutable {
+    if (i != 5 && j != 5)
+      clang_analyzer_warnIfReached();
+    ++i;
+    ++j;
+  }();
+  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
+  [=]() mutable {
+    if (i != 5 && j != 6)
+      clang_analyzer_warnIfReached();
+    ++i;
+    ++j;
+  }();
+  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j == 6); // expected-warning{{TRUE}}
+  [&]() mutable {
+    if (i != 5 && j != 6)
+      clang_analyzer_warnIfReached();
+    ++i;
+    ++j;
+  }();
+  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
+  clang_analyzer_eval(j == 7); // expected-warning{{TRUE}}
+}
+
+void testReturnValue() {
+  int i = 5;
+  auto l = [i] (int a) {
+    return i + a;
+  };
+  int b = l(3);
+  clang_analyzer_eval(b == 8); // expected-warning{{TRUE}}
+}
+
+// Nested lambdas.
+
+void testNestedLambdas() {
+  int i = 5;
+  auto l = [i]() mutable {
+    [&i]() {
+      ++i;
+    }();
+    if (i != 6)
+      clang_analyzer_warnIfReached();
+  };
+  l();
+  clang_analyzer_eval(i == 5); // expected-warning{{TRUE}}
+}
+
+// Captured this.
+
+class RandomClass {
+  int i;
+
+  void captureFields() {
+    i = 5;
+    [this]() {
+      // clang_analyzer_eval does nothing in inlined functions.
+      if (i != 5)
+        clang_analyzer_warnIfReached();
+      ++i;
+    }();
+    clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
+  }
+};
+
+
+// Nested this capture.
+
+class RandomClass2 {
+  int i;
+
+  void captureFields() {
+    i = 5;
+    [this]() {
+      // clang_analyzer_eval does nothing in inlined functions.
+      if (i != 5)
+        clang_analyzer_warnIfReached();
+      ++i;
+      [this]() {
+        // clang_analyzer_eval does nothing in inlined functions.
+        if (i != 6)
+          clang_analyzer_warnIfReached();
+        ++i;
+      }();
+    }();
+    clang_analyzer_eval(i == 7); // expected-warning{{TRUE}}
+  }
+};
+
+
+// Captured function pointers.
+
+void inc(int &x) {
+  ++x;
+}
+
+void testFunctionPointerCapture() {
+  void (*func)(int &) = inc;
+  int i = 5;
+  [&i, func] {
+    func(i);
+  }();
+  clang_analyzer_eval(i == 6); // expected-warning{{TRUE}}
+}
+
+
+// Test inline defensive checks
+int getNum();
+
+void inlineDefensiveChecks() {
+  int i = getNum();
+  [=]() {
+    if (i == 0)
+      ;
+  }();
+  int p = 5/i;
+  (void)p;
+}
+
 // CHECK: [B2 (ENTRY)]
 // CHECK:   Succs (1): B1
 // CHECK: [B1]

Modified: cfe/trunk/test/Analysis/temporaries.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=247426&r1=247425&r2=247426&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/temporaries.cpp (original)
+++ cfe/trunk/test/Analysis/temporaries.cpp Fri Sep 11 11:55:01 2015
@@ -299,13 +299,7 @@ namespace destructors {
   void testRecursiveFramesStart() { testRecursiveFrames(false); }
 
   void testLambdas() {
-    // This is the test we would like to write:
-    // []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
-    // But currently the analyzer stops when it encounters a lambda:
-    [] {};
-    // The CFG for this now looks correct, but we still do not reach the line
-    // below.
-    clang_analyzer_warnIfReached(); // FIXME: Should warn.
+    []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
   }
 
   void testGnuExpressionStatements(int v) {




More information about the cfe-commits mailing list