diff --git a/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.cs b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.cs
new file mode 100644
index 00000000000..8d10f24b067
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.cs
@@ -0,0 +1,190 @@
+using System;
+
+public class LambdaFlow
+{
+ ///
+ /// Flow into a normal method
+ ///
+ class Ex1
+ {
+ void M1(string s)
+ {
+ Sink(s); // $ hasValueFlow=1
+ }
+
+ public void M2()
+ {
+ var source = Source(1);
+ M1(source);
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// Flow into a lambda
+ ///
+ class Ex2
+ {
+ void M1(Action lambda)
+ {
+ var source = Source(2);
+ lambda(source);
+ }
+
+ void M2()
+ {
+ Action lambda = x => Sink(x); // $ hasValueFlow=2
+ M1(lambda);
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// Flow out of a lambda
+ ///
+ class Ex3
+ {
+ Func M1()
+ {
+ return () => Source(3);
+ }
+
+ void M2()
+ {
+ var lambda = M1();
+ Sink(lambda()); // $ hasValueFlow=3
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// Flow through a lambda
+ ///
+ class Ex4
+ {
+ string M1(Func lambda, string input)
+ {
+ return lambda(input);
+ }
+
+ void M2()
+ {
+ Func id = x => x;
+ var source = Source(4);
+ var output = M1(id, source);
+ Sink(output); // $ hasValueFlow=4
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// No flow into lambda (call context sensitivity)
+ ///
+ class Ex5
+ {
+ void M1(Action lambda, string input)
+ {
+ lambda(input);
+ }
+
+ void M2(Action lambda, string input)
+ {
+ M1(lambda, input);
+ }
+
+ void M3()
+ {
+ Action lambda1 = arg => Sink(arg);
+ Action lambda2 = arg => { };
+
+ var source = Source(5);
+ var nonSource = "non-source";
+
+ M1(lambda1, nonSource);
+ M1(lambda2, source);
+
+ M2(lambda1, nonSource);
+ M2(lambda2, source);
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// Flow into a returned lambda
+ ///
+ class Ex6
+ {
+ Action M1()
+ {
+ return x => Sink(x); // $ hasValueFlow=6
+ }
+
+ void M2()
+ {
+ var source = Source(6);
+ var lambda = M1();
+ lambda(source);
+ }
+ }
+
+
+
+
+
+
+
+ ///
+ /// No flow through lambda
+ ///
+ class Ex7
+ {
+ void M1(Func lambda)
+ {
+ var source = Source(7);
+ lambda(source);
+ }
+
+ void M2(Func lambda)
+ {
+ var nonSource = "non-source";
+ var output = lambda(nonSource);
+ Sink(output);
+ }
+
+ void M3()
+ {
+ Func id = x => x;
+ M1(id);
+ M2(id);
+ }
+ }
+
+ static string Source(int source) => source.ToString();
+
+ static void Sink(string value) { }
+}
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.expected b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.expected
new file mode 100644
index 00000000000..a24a0dd550e
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.expected
@@ -0,0 +1,120 @@
+models
+edges
+| LambdaFlow.cs:10:24:10:24 | s : String | LambdaFlow.cs:12:18:12:18 | access to parameter s | provenance | |
+| LambdaFlow.cs:17:17:17:22 | access to local variable source : String | LambdaFlow.cs:18:16:18:21 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:17:26:17:34 | call to method Source : String | LambdaFlow.cs:17:17:17:22 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:18:16:18:21 | access to local variable source : String | LambdaFlow.cs:10:24:10:24 | s : String | provenance | |
+| LambdaFlow.cs:33:32:33:37 | lambda [Return] : Action [delegate argument at position 0] : String | LambdaFlow.cs:42:16:42:21 | [post] access to local variable lambda : Action [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:35:17:35:22 | access to local variable source : String | LambdaFlow.cs:36:20:36:25 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:35:26:35:34 | call to method Source : String | LambdaFlow.cs:35:17:35:22 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:36:13:36:18 | [post] access to parameter lambda : Action [delegate argument at position 0] : String | LambdaFlow.cs:33:32:33:37 | lambda [Return] : Action [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:36:20:36:25 | access to local variable source : String | LambdaFlow.cs:36:13:36:18 | [post] access to parameter lambda : Action [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:41:37:41:37 | x : String | LambdaFlow.cs:41:47:41:47 | access to parameter x | provenance | |
+| LambdaFlow.cs:42:16:42:21 | [post] access to local variable lambda : Action [delegate argument at position 0] : String | LambdaFlow.cs:41:37:41:37 | x : String | provenance | |
+| LambdaFlow.cs:59:20:59:34 | (...) => ... : (...) => ... [delegate return] : String | LambdaFlow.cs:64:26:64:29 | call to method M1 : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:59:26:59:34 | call to method Source : String | LambdaFlow.cs:59:20:59:34 | (...) => ... : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:64:17:64:22 | access to local variable lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:65:18:65:23 | access to local variable lambda : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:64:26:64:29 | call to method M1 : (...) => ... [delegate return] : String | LambdaFlow.cs:64:17:64:22 | access to local variable lambda : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:65:18:65:23 | access to local variable lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:65:18:65:25 | delegate call | provenance | |
+| LambdaFlow.cs:80:40:80:45 | lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:82:20:82:25 | access to parameter lambda : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:80:55:80:59 | input : String | LambdaFlow.cs:82:27:82:31 | access to parameter input : String | provenance | |
+| LambdaFlow.cs:82:20:82:25 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | LambdaFlow.cs:80:40:80:45 | lambda [Return] : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:82:20:82:25 | access to parameter lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:82:20:82:32 | delegate call : String | provenance | |
+| LambdaFlow.cs:82:27:82:31 | access to parameter input : String | LambdaFlow.cs:82:20:82:25 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:87:34:87:35 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:89:29:89:30 | access to local variable id : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:87:39:87:39 | x : String | LambdaFlow.cs:87:44:87:44 | access to parameter x : String | provenance | |
+| LambdaFlow.cs:87:39:87:44 | (...) => ... : (...) => ... [delegate return] : String | LambdaFlow.cs:87:34:87:35 | access to local variable id : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:88:17:88:22 | access to local variable source : String | LambdaFlow.cs:89:33:89:38 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:88:26:88:34 | call to method Source : String | LambdaFlow.cs:88:17:88:22 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:89:17:89:22 | access to local variable output : String | LambdaFlow.cs:90:18:90:23 | access to local variable output | provenance | |
+| LambdaFlow.cs:89:26:89:39 | call to method M1 : String | LambdaFlow.cs:89:17:89:22 | access to local variable output : String | provenance | |
+| LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:87:39:87:39 | x : String | provenance | |
+| LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:87:39:87:44 | (...) => ... : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:89:29:89:30 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:80:40:80:45 | lambda : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:89:29:89:30 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:89:26:89:39 | call to method M1 : String | provenance | |
+| LambdaFlow.cs:89:33:89:38 | access to local variable source : String | LambdaFlow.cs:80:55:80:59 | input : String | provenance | |
+| LambdaFlow.cs:89:33:89:38 | access to local variable source : String | LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:166:38:166:43 | lambda [Return] : Func [delegate argument at position 0] : String | LambdaFlow.cs:182:16:182:17 | [post] access to local variable id : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:168:17:168:22 | access to local variable source : String | LambdaFlow.cs:169:20:169:25 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:168:26:168:34 | call to method Source : String | LambdaFlow.cs:168:17:168:22 | access to local variable source : String | provenance | |
+| LambdaFlow.cs:169:13:169:18 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | LambdaFlow.cs:166:38:166:43 | lambda [Return] : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:169:20:169:25 | access to local variable source : String | LambdaFlow.cs:169:13:169:18 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | provenance | |
+| LambdaFlow.cs:172:38:172:43 | lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:175:26:175:31 | access to parameter lambda : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:175:17:175:22 | access to local variable output : String | LambdaFlow.cs:176:18:176:23 | access to local variable output | provenance | |
+| LambdaFlow.cs:175:26:175:31 | access to parameter lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:175:26:175:42 | delegate call : String | provenance | |
+| LambdaFlow.cs:175:26:175:42 | delegate call : String | LambdaFlow.cs:175:17:175:22 | access to local variable output : String | provenance | |
+| LambdaFlow.cs:181:34:181:35 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:183:16:183:17 | access to local variable id : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:181:39:181:39 | x : String | LambdaFlow.cs:181:44:181:44 | access to parameter x : String | provenance | |
+| LambdaFlow.cs:181:39:181:44 | (...) => ... : (...) => ... [delegate return] : String | LambdaFlow.cs:181:34:181:35 | access to local variable id : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:182:16:182:17 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:181:39:181:39 | x : String | provenance | |
+| LambdaFlow.cs:182:16:182:17 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:181:39:181:44 | (...) => ... : (...) => ... [delegate return] : String | provenance | |
+| LambdaFlow.cs:183:16:183:17 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:172:38:172:43 | lambda : (...) => ... [delegate return] : String | provenance | |
+nodes
+| LambdaFlow.cs:10:24:10:24 | s : String | semmle.label | s : String |
+| LambdaFlow.cs:12:18:12:18 | access to parameter s | semmle.label | access to parameter s |
+| LambdaFlow.cs:17:17:17:22 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:17:26:17:34 | call to method Source : String | semmle.label | call to method Source : String |
+| LambdaFlow.cs:18:16:18:21 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:33:32:33:37 | lambda [Return] : Action [delegate argument at position 0] : String | semmle.label | lambda [Return] : Action [delegate argument at position 0] : String |
+| LambdaFlow.cs:35:17:35:22 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:35:26:35:34 | call to method Source : String | semmle.label | call to method Source : String |
+| LambdaFlow.cs:36:13:36:18 | [post] access to parameter lambda : Action [delegate argument at position 0] : String | semmle.label | [post] access to parameter lambda : Action [delegate argument at position 0] : String |
+| LambdaFlow.cs:36:20:36:25 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:41:37:41:37 | x : String | semmle.label | x : String |
+| LambdaFlow.cs:41:47:41:47 | access to parameter x | semmle.label | access to parameter x |
+| LambdaFlow.cs:42:16:42:21 | [post] access to local variable lambda : Action [delegate argument at position 0] : String | semmle.label | [post] access to local variable lambda : Action [delegate argument at position 0] : String |
+| LambdaFlow.cs:59:20:59:34 | (...) => ... : (...) => ... [delegate return] : String | semmle.label | (...) => ... : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:59:26:59:34 | call to method Source : String | semmle.label | call to method Source : String |
+| LambdaFlow.cs:64:17:64:22 | access to local variable lambda : (...) => ... [delegate return] : String | semmle.label | access to local variable lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:64:26:64:29 | call to method M1 : (...) => ... [delegate return] : String | semmle.label | call to method M1 : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:65:18:65:23 | access to local variable lambda : (...) => ... [delegate return] : String | semmle.label | access to local variable lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:65:18:65:25 | delegate call | semmle.label | delegate call |
+| LambdaFlow.cs:80:40:80:45 | lambda : (...) => ... [delegate return] : String | semmle.label | lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:80:40:80:45 | lambda [Return] : Func [delegate argument at position 0] : String | semmle.label | lambda [Return] : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:80:55:80:59 | input : String | semmle.label | input : String |
+| LambdaFlow.cs:82:20:82:25 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | semmle.label | [post] access to parameter lambda : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:82:20:82:25 | access to parameter lambda : (...) => ... [delegate return] : String | semmle.label | access to parameter lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:82:20:82:32 | delegate call : String | semmle.label | delegate call : String |
+| LambdaFlow.cs:82:27:82:31 | access to parameter input : String | semmle.label | access to parameter input : String |
+| LambdaFlow.cs:87:34:87:35 | access to local variable id : (...) => ... [delegate return] : String | semmle.label | access to local variable id : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:87:39:87:39 | x : String | semmle.label | x : String |
+| LambdaFlow.cs:87:39:87:44 | (...) => ... : (...) => ... [delegate return] : String | semmle.label | (...) => ... : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:87:44:87:44 | access to parameter x : String | semmle.label | access to parameter x : String |
+| LambdaFlow.cs:88:17:88:22 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:88:26:88:34 | call to method Source : String | semmle.label | call to method Source : String |
+| LambdaFlow.cs:89:17:89:22 | access to local variable output : String | semmle.label | access to local variable output : String |
+| LambdaFlow.cs:89:26:89:39 | call to method M1 : String | semmle.label | call to method M1 : String |
+| LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String | semmle.label | [post] access to local variable id : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:89:29:89:30 | access to local variable id : (...) => ... [delegate return] : String | semmle.label | access to local variable id : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:89:33:89:38 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:90:18:90:23 | access to local variable output | semmle.label | access to local variable output |
+| LambdaFlow.cs:166:38:166:43 | lambda [Return] : Func [delegate argument at position 0] : String | semmle.label | lambda [Return] : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:168:17:168:22 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:168:26:168:34 | call to method Source : String | semmle.label | call to method Source : String |
+| LambdaFlow.cs:169:13:169:18 | [post] access to parameter lambda : Func [delegate argument at position 0] : String | semmle.label | [post] access to parameter lambda : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:169:20:169:25 | access to local variable source : String | semmle.label | access to local variable source : String |
+| LambdaFlow.cs:172:38:172:43 | lambda : (...) => ... [delegate return] : String | semmle.label | lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:175:17:175:22 | access to local variable output : String | semmle.label | access to local variable output : String |
+| LambdaFlow.cs:175:26:175:31 | access to parameter lambda : (...) => ... [delegate return] : String | semmle.label | access to parameter lambda : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:175:26:175:42 | delegate call : String | semmle.label | delegate call : String |
+| LambdaFlow.cs:176:18:176:23 | access to local variable output | semmle.label | access to local variable output |
+| LambdaFlow.cs:181:34:181:35 | access to local variable id : (...) => ... [delegate return] : String | semmle.label | access to local variable id : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:181:39:181:39 | x : String | semmle.label | x : String |
+| LambdaFlow.cs:181:39:181:44 | (...) => ... : (...) => ... [delegate return] : String | semmle.label | (...) => ... : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:181:44:181:44 | access to parameter x : String | semmle.label | access to parameter x : String |
+| LambdaFlow.cs:182:16:182:17 | [post] access to local variable id : Func [delegate argument at position 0] : String | semmle.label | [post] access to local variable id : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:183:16:183:17 | access to local variable id : (...) => ... [delegate return] : String | semmle.label | access to local variable id : (...) => ... [delegate return] : String |
+subpaths
+| LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:87:39:87:39 | x : String | LambdaFlow.cs:87:44:87:44 | access to parameter x : String | LambdaFlow.cs:87:39:87:44 | (...) => ... : (...) => ... [delegate return] : String |
+| LambdaFlow.cs:89:29:89:30 | access to local variable id : (...) => ... [delegate return] : String | LambdaFlow.cs:80:40:80:45 | lambda : (...) => ... [delegate return] : String | LambdaFlow.cs:82:20:82:32 | delegate call : String | LambdaFlow.cs:89:26:89:39 | call to method M1 : String |
+| LambdaFlow.cs:89:33:89:38 | access to local variable source : String | LambdaFlow.cs:80:55:80:59 | input : String | LambdaFlow.cs:80:40:80:45 | lambda [Return] : Func [delegate argument at position 0] : String | LambdaFlow.cs:89:29:89:30 | [post] access to local variable id : Func [delegate argument at position 0] : String |
+| LambdaFlow.cs:182:16:182:17 | [post] access to local variable id : Func [delegate argument at position 0] : String | LambdaFlow.cs:181:39:181:39 | x : String | LambdaFlow.cs:181:44:181:44 | access to parameter x : String | LambdaFlow.cs:181:39:181:44 | (...) => ... : (...) => ... [delegate return] : String |
+testFailures
+| LambdaFlow.cs:144:34:144:52 | // ... | Missing result: hasValueFlow=6 |
+| LambdaFlow.cs:176:18:176:23 | access to local variable output | Unexpected result: hasValueFlow=7 |
+#select
+| LambdaFlow.cs:12:18:12:18 | access to parameter s | LambdaFlow.cs:17:26:17:34 | call to method Source : String | LambdaFlow.cs:12:18:12:18 | access to parameter s | $@ | LambdaFlow.cs:17:26:17:34 | call to method Source : String | call to method Source : String |
+| LambdaFlow.cs:41:47:41:47 | access to parameter x | LambdaFlow.cs:35:26:35:34 | call to method Source : String | LambdaFlow.cs:41:47:41:47 | access to parameter x | $@ | LambdaFlow.cs:35:26:35:34 | call to method Source : String | call to method Source : String |
+| LambdaFlow.cs:65:18:65:25 | delegate call | LambdaFlow.cs:59:26:59:34 | call to method Source : String | LambdaFlow.cs:65:18:65:25 | delegate call | $@ | LambdaFlow.cs:59:26:59:34 | call to method Source : String | call to method Source : String |
+| LambdaFlow.cs:90:18:90:23 | access to local variable output | LambdaFlow.cs:88:26:88:34 | call to method Source : String | LambdaFlow.cs:90:18:90:23 | access to local variable output | $@ | LambdaFlow.cs:88:26:88:34 | call to method Source : String | call to method Source : String |
+| LambdaFlow.cs:176:18:176:23 | access to local variable output | LambdaFlow.cs:168:26:168:34 | call to method Source : String | LambdaFlow.cs:176:18:176:23 | access to local variable output | $@ | LambdaFlow.cs:168:26:168:34 | call to method Source : String | call to method Source : String |
diff --git a/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.ql b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.ql
new file mode 100644
index 00000000000..f47c9f4e9a4
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/lambda/LambdaFlow.ql
@@ -0,0 +1,12 @@
+/**
+ * @kind path-problem
+ */
+
+import csharp
+import TestUtilities.InlineFlowTest
+import ValueFlowTest
+import PathGraph
+
+from PathNode source, PathNode sink
+where flowPath(source, sink)
+select sink, source, sink, "$@", source, source.toString()