Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for data flow through positional patterns in `is` expressions and `switch` cases.
Original file line number Diff line number Diff line change
Expand Up @@ -563,13 +563,19 @@ module LocalFlow {
exists(IsExpr e |
e1 = e.getExpr() and
e2 = e.getPattern() and
e2 instanceof TuplePatternExpr
(
e2 instanceof TuplePatternExpr or
e2 instanceof RecursivePatternExpr
)
)
or
exists(Switch sw |
e1 = sw.getExpr() and
e2 = sw.getACase().getPattern() and
e2 instanceof TuplePatternExpr
(
e2 instanceof TuplePatternExpr or
e2 instanceof RecursivePatternExpr
)
)
}

Expand Down Expand Up @@ -2208,6 +2214,40 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
storeStepDelegateCall(node1, c, node2)
}

private predicate readStepPattern(Node node1, Content c, Node node2) {
exists(RecursivePatternExpr pattern, PatternExpr item, int i |
node1.asExpr() = pattern and
item = pattern.getPositionalPatterns().getPattern(i) and
c.(FieldContent).getField() =
pattern.getType().(TupleType).getElement(i).getUnboundDeclaration()
|
// item = { ... } in node1 = (var ..., { ... })
item = node2.asExpr().(RecursivePatternExpr)
or
// item = (...,...) in node1 = (var ..., (..., ...))
item = node2.asExpr().(TuplePatternExpr)
or
// item = variable in node1 = (..., variable, ...) in a case/is (var ..., var ...)
exists(AssignableDefinitions::PatternDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
item = lvd.getDeclaration()
)
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
exists(TuplePatternExpr pattern, PatternExpr item, int i |
node1.asExpr() = pattern and
c.(FieldContent).getField() =
pattern.getType().(TupleType).getElement(i).getUnboundDeclaration() and
item = pattern.getArgument(i)
|
exists(AssignableDefinitions::PatternDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
item = lvd.getDeclaration()
)
)
}

private predicate readContentStep(Node node1, Content c, Node node2) {
arrayRead(node1.asExpr(), node2.asExpr()) and
c instanceof ElementContent
Expand Down Expand Up @@ -2246,15 +2286,10 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
te instanceof TuplePatternExpr and
exists(AssignableDefinitions::PatternDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item
)
)
or
readStepPattern(node1, c, node2)
or
VariableCapture::readStep(node1, c, node2)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
| Tuples.cs:75:13:75:30 | SSA qualifier def(y.Item2.Item2) | Tuples.cs:80:22:80:34 | access to field Item2 |
| Tuples.cs:75:17:75:30 | (..., ...) | Tuples.cs:75:13:75:13 | access to local variable y |
| Tuples.cs:75:18:75:18 | access to parameter s | Tuples.cs:75:25:75:25 | access to parameter s |
| Tuples.cs:76:17:76:17 | access to local variable y | Tuples.cs:78:18:78:39 | { ... } |
| Tuples.cs:76:17:76:17 | access to local variable y | Tuples.cs:79:22:79:22 | access to local variable y |
| Tuples.cs:78:19:78:23 | SSA def(a) | Tuples.cs:78:46:78:46 | access to local variable a |
| Tuples.cs:78:19:78:23 | String a | Tuples.cs:78:19:78:23 | SSA def(a) |
Expand Down Expand Up @@ -226,4 +227,42 @@
| Tuples.cs:133:24:133:29 | (..., ...) | Tuples.cs:133:9:133:20 | (..., ...) |
| Tuples.cs:133:25:133:25 | 1 | Tuples.cs:133:9:133:29 | ... = ... |
| Tuples.cs:133:28:133:28 | access to local variable o | Tuples.cs:133:18:133:19 | access to local variable y4 |
| Tuples.cs:144:18:144:31 | this | Tuples.cs:144:18:144:31 | this access |
| Tuples.cs:139:13:139:14 | access to local variable o1 | Tuples.cs:139:13:139:35 | SSA def(o1) |
| Tuples.cs:139:13:139:35 | SSA def(o1) | Tuples.cs:141:18:141:19 | access to local variable o1 |
| Tuples.cs:139:18:139:35 | call to method Source<String> | Tuples.cs:139:13:139:14 | access to local variable o1 |
| Tuples.cs:139:33:139:34 | 10 | Tuples.cs:139:33:139:34 | (...) ... |
| Tuples.cs:140:13:140:14 | access to local variable o2 | Tuples.cs:140:13:140:35 | SSA def(o2) |
| Tuples.cs:140:13:140:35 | SSA def(o2) | Tuples.cs:141:26:141:27 | access to local variable o2 |
| Tuples.cs:140:18:140:35 | call to method Source<String> | Tuples.cs:140:13:140:14 | access to local variable o2 |
| Tuples.cs:140:33:140:34 | 11 | Tuples.cs:140:33:140:34 | (...) ... |
| Tuples.cs:141:13:141:13 | access to local variable x | Tuples.cs:141:13:141:32 | SSA def(x) |
| Tuples.cs:141:13:141:32 | SSA def(x) | Tuples.cs:142:17:142:17 | access to local variable x |
| Tuples.cs:141:17:141:32 | (..., ...) | Tuples.cs:141:13:141:13 | access to local variable x |
| Tuples.cs:142:17:142:17 | access to local variable x | Tuples.cs:144:18:144:46 | { ... } |
| Tuples.cs:144:19:144:24 | SSA def(a1) | Tuples.cs:145:22:145:23 | access to local variable a1 |
| Tuples.cs:144:19:144:24 | String a1 | Tuples.cs:144:19:144:24 | SSA def(a1) |
| Tuples.cs:144:28:144:33 | Int32 b1 | Tuples.cs:144:28:144:33 | SSA def(b1) |
| Tuples.cs:144:28:144:33 | SSA def(b1) | Tuples.cs:147:22:147:23 | access to local variable b1 |
| Tuples.cs:144:36:144:41 | SSA def(c1) | Tuples.cs:146:22:146:23 | access to local variable c1 |
| Tuples.cs:144:36:144:41 | String c1 | Tuples.cs:144:36:144:41 | SSA def(c1) |
| Tuples.cs:147:22:147:23 | access to local variable b1 | Tuples.cs:147:22:147:23 | (...) ... |
| Tuples.cs:151:13:151:14 | access to local variable o3 | Tuples.cs:151:13:151:35 | SSA def(o3) |
| Tuples.cs:151:13:151:35 | SSA def(o3) | Tuples.cs:153:18:153:19 | access to local variable o3 |
| Tuples.cs:151:18:151:35 | call to method Source<String> | Tuples.cs:151:13:151:14 | access to local variable o3 |
| Tuples.cs:151:33:151:34 | 12 | Tuples.cs:151:33:151:34 | (...) ... |
| Tuples.cs:152:13:152:14 | access to local variable o4 | Tuples.cs:152:13:152:35 | SSA def(o4) |
| Tuples.cs:152:13:152:35 | SSA def(o4) | Tuples.cs:153:26:153:27 | access to local variable o4 |
| Tuples.cs:152:18:152:35 | call to method Source<String> | Tuples.cs:152:13:152:14 | access to local variable o4 |
| Tuples.cs:152:33:152:34 | 13 | Tuples.cs:152:33:152:34 | (...) ... |
| Tuples.cs:153:13:153:13 | access to local variable y | Tuples.cs:153:13:153:32 | SSA def(y) |
| Tuples.cs:153:13:153:32 | SSA def(y) | Tuples.cs:154:13:154:13 | access to local variable y |
| Tuples.cs:153:17:153:32 | (..., ...) | Tuples.cs:153:13:153:13 | access to local variable y |
| Tuples.cs:154:13:154:13 | access to local variable y | Tuples.cs:154:18:154:42 | { ... } |
| Tuples.cs:154:19:154:24 | SSA def(a2) | Tuples.cs:156:18:156:19 | access to local variable a2 |
| Tuples.cs:154:19:154:24 | String a2 | Tuples.cs:154:19:154:24 | SSA def(a2) |
| Tuples.cs:154:32:154:33 | Int32 b2 | Tuples.cs:154:32:154:33 | SSA def(b2) |
| Tuples.cs:154:32:154:33 | SSA def(b2) | Tuples.cs:158:18:158:19 | access to local variable b2 |
| Tuples.cs:154:36:154:37 | SSA def(c2) | Tuples.cs:157:18:157:19 | access to local variable c2 |
| Tuples.cs:154:36:154:37 | String c2 | Tuples.cs:154:36:154:37 | SSA def(c2) |
| Tuples.cs:158:18:158:19 | access to local variable b2 | Tuples.cs:158:18:158:19 | (...) ... |
| Tuples.cs:169:18:169:31 | this | Tuples.cs:169:18:169:31 | this access |
120 changes: 107 additions & 13 deletions csharp/ql/test/library-tests/dataflow/tuples/PrintAst.expected
Original file line number Diff line number Diff line change
Expand Up @@ -504,20 +504,114 @@ Tuples.cs:
# 134| 12: [ExprStmt] ...;
# 134| 0: [MethodCall] call to method Sink
# 134| 0: [LocalVariableAccess] access to local variable y4
# 137| 13: [Method] Sink
# 137| 13: [Method] M7
# 137| -1: [TypeMention] Void
# 138| 4: [BlockStmt] {...}
# 139| 0: [LocalVariableDeclStmt] ... ...;
# 139| 0: [LocalVariableDeclAndInitExpr] String o1 = ...
# 139| -1: [TypeMention] string
# 139| 0: [LocalVariableAccess] access to local variable o1
# 139| 1: [MethodCall] call to method Source<String>
# 139| 0: [CastExpr] (...) ...
# 139| 1: [IntLiteral] 10
# 140| 1: [LocalVariableDeclStmt] ... ...;
# 140| 0: [LocalVariableDeclAndInitExpr] String o2 = ...
# 140| -1: [TypeMention] string
# 140| 0: [LocalVariableAccess] access to local variable o2
# 140| 1: [MethodCall] call to method Source<String>
# 140| 0: [CastExpr] (...) ...
# 140| 1: [IntLiteral] 11
# 141| 2: [LocalVariableDeclStmt] ... ...;
# 141| 0: [LocalVariableDeclAndInitExpr] (String,(Int32,String),Int32) x = ...
# 141| -1: [TypeMention] (string, (int, string), int)
# 141| 0: [LocalVariableAccess] access to local variable x
# 141| 1: [TupleExpr] (..., ...)
# 141| 0: [LocalVariableAccess] access to local variable o1
# 141| 1: [TupleExpr] (..., ...)
# 141| 0: [IntLiteral] 2
# 141| 1: [LocalVariableAccess] access to local variable o2
# 141| 2: [IntLiteral] 3
# 142| 3: [SwitchStmt] switch (...) {...}
# 142| 0: [LocalVariableAccess] access to local variable x
# 144| 0: [CaseStmt] case ...:
# 144| 0: [RecursivePatternExpr] { ... }
# 144| 2: [PositionalPatternExpr] ( ... )
# 144| 0: [VariablePatternExpr] String a1
# 144| 1: [RecursivePatternExpr] { ... }
# 144| 2: [PositionalPatternExpr] ( ... )
# 144| 0: [VariablePatternExpr] Int32 b1
# 144| 1: [VariablePatternExpr] String c1
# 144| 2: [DiscardPatternExpr] _
# 145| 1: [ExprStmt] ...;
# 145| 0: [MethodCall] call to method Sink
# 145| 0: [LocalVariableAccess] access to local variable a1
# 146| 2: [ExprStmt] ...;
# 146| 0: [MethodCall] call to method Sink
# 146| 0: [LocalVariableAccess] access to local variable c1
# 147| 3: [ExprStmt] ...;
# 147| 0: [MethodCall] call to method Sink
# 147| 0: [CastExpr] (...) ...
# 147| 1: [LocalVariableAccess] access to local variable b1
# 148| 4: [BreakStmt] break;
# 151| 4: [LocalVariableDeclStmt] ... ...;
# 151| 0: [LocalVariableDeclAndInitExpr] String o3 = ...
# 151| -1: [TypeMention] string
# 151| 0: [LocalVariableAccess] access to local variable o3
# 151| 1: [MethodCall] call to method Source<String>
# 151| 0: [CastExpr] (...) ...
# 151| 1: [IntLiteral] 12
# 152| 5: [LocalVariableDeclStmt] ... ...;
# 152| 0: [LocalVariableDeclAndInitExpr] String o4 = ...
# 152| -1: [TypeMention] string
# 152| 0: [LocalVariableAccess] access to local variable o4
# 152| 1: [MethodCall] call to method Source<String>
# 152| 0: [CastExpr] (...) ...
# 152| 1: [IntLiteral] 13
# 153| 6: [LocalVariableDeclStmt] ... ...;
# 153| 0: [LocalVariableDeclAndInitExpr] (String,(Int32,String),Int32) y = ...
# 153| -1: [TypeMention] (string, (int, string), int)
# 153| 0: [LocalVariableAccess] access to local variable y
# 153| 1: [TupleExpr] (..., ...)
# 153| 0: [LocalVariableAccess] access to local variable o3
# 153| 1: [TupleExpr] (..., ...)
# 153| 0: [IntLiteral] 2
# 153| 1: [LocalVariableAccess] access to local variable o4
# 153| 2: [IntLiteral] 3
# 154| 7: [IfStmt] if (...) ...
# 154| 0: [IsExpr] ... is ...
# 154| 0: [LocalVariableAccess] access to local variable y
# 154| 1: [RecursivePatternExpr] { ... }
# 154| 2: [PositionalPatternExpr] ( ... )
# 154| 0: [VariablePatternExpr] String a2
# 154| 1: [TuplePatternExpr] (..., ...)
# 154| 0: [VariablePatternExpr] Int32 b2
# 154| 1: [VariablePatternExpr] String c2
# 154| 2: [DiscardPatternExpr] _
# 155| 1: [BlockStmt] {...}
# 156| 0: [ExprStmt] ...;
# 156| 0: [MethodCall] call to method Sink
# 156| 0: [LocalVariableAccess] access to local variable a2
# 157| 1: [ExprStmt] ...;
# 157| 0: [MethodCall] call to method Sink
# 157| 0: [LocalVariableAccess] access to local variable c2
# 158| 2: [ExprStmt] ...;
# 158| 0: [MethodCall] call to method Sink
# 158| 0: [CastExpr] (...) ...
# 158| 1: [LocalVariableAccess] access to local variable b2
# 162| 14: [Method] Sink
# 162| -1: [TypeMention] Void
#-----| 2: (Parameters)
# 137| 0: [Parameter] o
# 137| -1: [TypeMention] object
# 137| 4: [BlockStmt] {...}
# 139| 16: [Method] Source`1
# 139| -1: [TypeMention] T
# 162| 0: [Parameter] o
# 162| -1: [TypeMention] object
# 162| 4: [BlockStmt] {...}
# 164| 17: [Method] Source`1
# 164| -1: [TypeMention] T
#-----| 1: (Type parameters)
# 139| 0: [TypeParameter] T
# 164| 0: [TypeParameter] T
#-----| 2: (Parameters)
# 139| 0: [Parameter] source
# 139| -1: [TypeMention] object
# 139| 4: [ThrowExpr] throw ...
# 139| 0: [NullLiteral] null
# 142| [NamespaceDeclaration] namespace ... { ... }
# 144| 1: [Class] IsExternalInit
# 164| 0: [Parameter] source
# 164| -1: [TypeMention] object
# 164| 4: [ThrowExpr] throw ...
# 164| 0: [NullLiteral] null
# 167| [NamespaceDeclaration] namespace ... { ... }
# 169| 1: [Class] IsExternalInit
25 changes: 25 additions & 0 deletions csharp/ql/test/library-tests/dataflow/tuples/Tuples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,31 @@ static void M6()
Sink(y4); // $ hasValueFlow=9
}

static void M7()
{
var o1 = Source<string>(10);
var o2 = Source<string>(11);
var x = (o1, (2, o2), 3);
switch (x)
{
case (var a1, (var b1, var c1), _):
Sink(a1); // $ hasValueFlow=10
Sink(c1); // $ hasValueFlow=11
Sink(b1);
break;
}

var o3 = Source<string>(12);
var o4 = Source<string>(13);
var y = (o3, (2, o4), 3);
if (y is (var a2, var (b2, c2), _))
{
Sink(a2); // $ hasValueFlow=12
Sink(c2); // $ hasValueFlow=13
Sink(b2);
}
}

public static void Sink(object o) { }

static T Source<T>(object source) => throw null;
Expand Down
Loading
Loading