Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-28 Thread Anna Zaks via cfe-commits
zaks.anna added a comment.

Looks like this patch is causing regressions:
https://llvm.org/bugs/show_bug.cgi?id=24914


Repository:
  rL LLVM

http://reviews.llvm.org/D12652



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-11 Thread Gábor Horváth via cfe-commits
xazax.hun added inline comments.


Comment at: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h:515-517
@@ -511,1 +514,5 @@
 
+  /// Returns true if lambdas should be inlined. Otherwise a sink node will be
+  /// generated each time a LambdaExpr is visited.
+  bool shouldInlineLambdas();
+

jordan_rose wrote:
> "inline" is kind of a misnomer, since we may not actually inline lambdas. I 
> would have suggested "model lambdas" or "lambda support".
Even when this configuration option is set to false, the body of the lambda is 
analyzed as a top level function. For this reason I think the "lambda support" 
might be a misnomer too. What do you think?


Comment at: lib/StaticAnalyzer/Core/MemRegion.cpp:740-741
@@ -739,3 +739,4 @@
   const DeclContext *DC,
-  const VarDecl *VD) {
+  const VarDecl *VD,
+  MemRegionManager *Mmgr) {
   while (LC) {

jordan_rose wrote:
> Why the extra parameter?
This is just a leftover from code evolution, thank you for spotting this.


http://reviews.llvm.org/D12652



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-11 Thread Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL247426: [Static Analyzer] Lambda support. (authored by 
xazax).

Changed prior to commit:
  http://reviews.llvm.org/D12652?vs=34498=34555#toc

Repository:
  rL LLVM

http://reviews.llvm.org/D12652

Files:
  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/lambda-notes.cpp
  cfe/trunk/test/Analysis/lambdas.cpp
  cfe/trunk/test/Analysis/temporaries.cpp

Index: cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===
--- cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -256,6 +256,9 @@
   /// \sa getMaxNodesPerTopLevelFunction
   Optional MaxNodesPerTopLevelFunction;
 
+  /// \sa shouldInlineLambdas
+  Optional 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 @@
   /// 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),
Index: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -341,6 +341,10 @@
   void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, 
   ExplodedNodeSet );
 
+  /// VisitLambdaExpr - Transfer function logic for LambdaExprs.
+  void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, 
+   ExplodedNodeSet );
+
   /// VisitBinaryOperator - Transfer function logic for binary operators.
   void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, 
ExplodedNodeSet );
Index: cfe/trunk/test/Analysis/dead-stores.cpp
===
--- cfe/trunk/test/Analysis/dead-stores.cpp
+++ cfe/trunk/test/Analysis/dead-stores.cpp
@@ -174,3 +174,17 @@
   return radar13213575_testit(5) + radar13213575_testit(3);
 }
 
+//===--===//
+// Dead store checking involving lambdas.
+//===--===//
+
+int basicLambda(int i, int j) {
+  i = 5; // no warning
+  j = 6; // no warning
+  [i] { (void)i; }();
+  [] { (void)j; }();
+  i = 2;
+  j = 3;
+  return i + j;
+}
+
Index: cfe/trunk/test/Analysis/lambda-notes.cpp
===
--- cfe/trunk/test/Analysis/lambda-notes.cpp
+++ cfe/trunk/test/Analysis/lambda-notes.cpp
@@ -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:  
+// CHECK:   
+// CHECK:path
+// CHECK:
+// CHECK: 
+// CHECK:  kindcontrol
+// CHECK:  edges
+// CHECK:   
+// CHECK:
+// CHECK: start
+// CHECK:  
+// CHECK:   
+// CHECK:line8
+// CHECK:col3
+// CHECK:file0
+// CHECK:   
+// CHECK:   
+// CHECK:line8
+// CHECK:col5
+// CHECK:file0
+// CHECK:   
+// CHECK:  
+// CHECK: end
+// CHECK:  
+// CHECK:   
+// CHECK:line9
+// CHECK:col3
+// CHECK:file0
+// CHECK:   
+// CHECK:   
+// CHECK:line9
+// CHECK:col3
+// CHECK:file0
+// CHECK:   
+// CHECK:  
+// CHECK:
+// CHECK:   
+// CHECK: 
+// CHECK: 
+// CHECK:  kindevent
+// CHECK:  location
+// CHECK:  
+// CHECK:   line9
+// CHECK:   col3
+// CHECK:   file0
+// CHECK:  
+// CHECK:  ranges
+// CHECK:  
+// CHECK:
+// CHECK: 
+// CHECK:  line9
+// CHECK:  

Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-10 Thread Gábor Horváth via cfe-commits
xazax.hun updated this revision to Diff 34498.
xazax.hun added a comment.

- Updated to latest trunk
- Added test for the defensive inline check heuristic case
- Added test for diagnostic within a lambda body
- Added test for path notes


http://reviews.llvm.org/D12652

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

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -299,13 +299,7 @@
   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) {
Index: test/Analysis/lambdas.cpp
===
--- test/Analysis/lambdas.cpp
+++ test/Analysis/lambdas.cpp
@@ -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;
+  }();
+  [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  }();
+  [] {
+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 = [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  };
+  auto l3 = [] {
+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, ]() 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;
+}();
+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;
+  }();
+}();
+   

Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-10 Thread Jordan Rose via cfe-commits
jordan_rose added a comment.

A  few comments coming late…



Comment at: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h:515-517
@@ -511,1 +514,5 @@
 
+  /// Returns true if lambdas should be inlined. Otherwise a sink node will be
+  /// generated each time a LambdaExpr is visited.
+  bool shouldInlineLambdas();
+

"inline" is kind of a misnomer, since we may not actually inline lambdas. I 
would have suggested "model lambdas" or "lambda support".


Comment at: lib/StaticAnalyzer/Core/MemRegion.cpp:740-741
@@ -739,3 +739,4 @@
   const DeclContext *DC,
-  const VarDecl *VD) {
+  const VarDecl *VD,
+  MemRegionManager *Mmgr) {
   while (LC) {

Why the extra parameter?


http://reviews.llvm.org/D12652



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-10 Thread Anna Zaks via cfe-commits
zaks.anna added a comment.

Have you tested this on a large codebase that uses lambdas? When do you think 
we should turn this on by default?

Please, add test cases that demonstrate what happens when an issue is reported 
within a lambda and to check if inlined defensive checks work.

(As a follow up to this patch, we may need to teach LiveVariables.cpp and 
UninitializedValues.cpp about lambdas. For example, to address issues like this 
one: https://llvm.org/bugs/show_bug.cgi?id=22834)


http://reviews.llvm.org/D12652



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-10 Thread Anna Zaks via cfe-commits
zaks.anna accepted this revision.
zaks.anna added a comment.
This revision is now accepted and ready to land.

Please, do turn on by default.
LGTM


http://reviews.llvm.org/D12652



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


Re: [PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-08 Thread Gábor Horváth via cfe-commits
xazax.hun updated this revision to Diff 34285.
xazax.hun added a comment.

- Updated to newest trunk.
- Moved the feature behind an option.
- Fixed a crash when an operator() of a lambda is analyzed as a top level 
function, and a ThisExpr is referring to the this in the enclosing scope (this 
can only happen when lambda support is turned off).
- Added a new test case for nested lambdas capturing 'this'.


http://reviews.llvm.org/D12652

Files:
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/MemRegion.cpp
  test/Analysis/dead-stores.cpp
  test/Analysis/lambdas.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -299,13 +299,7 @@
   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) {
Index: test/Analysis/lambdas.cpp
===
--- test/Analysis/lambdas.cpp
+++ test/Analysis/lambdas.cpp
@@ -1,9 +1,167 @@
-// 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;
+  }();
+  [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  }();
+  [] {
+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 = [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  };
+  auto l3 = [] {
+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, ]() 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;
+}();
+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 

[PATCH] D12652: [Static Analyzer] Lambda support.

2015-09-04 Thread Gábor Horváth via cfe-commits
xazax.hun created this revision.
xazax.hun added reviewers: dcoughlin, zaks.anna, jordan_rose, ted.
xazax.hun added a subscriber: cfe-commits.

This patch adds lambda support for the static analyzer.
All of my initial tests are passed.

Before this patch each time a LambdaExpr was encountered a sink node was 
generated. This also reduced the coverage of code that is actually not inside a 
lambda operator(). Also the lack of lambda support is a known cause of some 
false positives (for example in the dead store checker).

This is a work in progress version of this patch, I will move this feature 
behind a flag and run it on LLVM to make sure it is also tested with real world 
code.


http://reviews.llvm.org/D12652

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  lib/StaticAnalyzer/Core/MemRegion.cpp
  test/Analysis/dead-stores.cpp
  test/Analysis/lambdas.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -299,13 +299,7 @@
   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) {
Index: test/Analysis/lambdas.cpp
===
--- test/Analysis/lambdas.cpp
+++ test/Analysis/lambdas.cpp
@@ -1,9 +1,142 @@
-// 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 -verify %s 
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG %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;
+  }();
+  [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  }();
+  [] {
+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 = [] {
+if (i != 5)
+  clang_analyzer_warnIfReached();
+  };
+  auto l3 = [] {
+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, ]() 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;
+}();
+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}}
+  }
+};
+
+// Captured function pointers.
+
+void inc(int ) {
+  ++x;
+}
+
+void testFunctionPointerCapture() {
+  void (*func)(int &) = inc;
+  int i = 5;
+  [, func] {
+func(i);
+  }();
+