Skip to content

Commit c867f2b

Browse files
authored
Merge pull request #10594 from michaelnebel/csharp/postupdatenotes
C#: Postupdate notes for ternary expressions.
2 parents 4a39bc8 + 95488bf commit c867f2b

File tree

10 files changed

+387
-42
lines changed

10 files changed

+387
-42
lines changed

csharp/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,7 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration {
3535
override predicate argHasPostUpdateExclude(ArgumentNode n) {
3636
n instanceof SummaryNode
3737
or
38-
n.asExpr().(Expr).stripCasts().getType() =
39-
any(Type t |
40-
not t instanceof RefType and
41-
not t = any(TypeParameter tp | not tp.isValueType())
42-
or
43-
t instanceof NullType
44-
)
38+
not exists(LocalFlow::getAPostUpdateNodeForArg(n.getControlFlowNode()))
4539
or
4640
n instanceof ImplicitCapturedArgumentNode
4741
or
@@ -50,5 +44,21 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration {
5044
n.asExpr() instanceof CIL::Expr
5145
}
5246

47+
override predicate postHasUniquePreExclude(PostUpdateNode n) {
48+
exists(ControlFlow::Nodes::ExprNode e, ControlFlow::Nodes::ExprNode arg |
49+
e = LocalFlow::getAPostUpdateNodeForArg(arg) and
50+
e != arg and
51+
n = TExprPostUpdateNode(e)
52+
)
53+
}
54+
55+
override predicate uniquePostUpdateExclude(Node n) {
56+
exists(ControlFlow::Nodes::ExprNode e, ControlFlow::Nodes::ExprNode arg |
57+
e = LocalFlow::getAPostUpdateNodeForArg(arg) and
58+
e != arg and
59+
n.asExpr() = arg.getExpr()
60+
)
61+
}
62+
5363
override predicate reverseReadExclude(Node n) { n.asExpr() = any(AwaitExpr ae).getExpr() }
5464
}

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,34 @@ module LocalFlow {
410410
n instanceof SummaryNode or
411411
n instanceof ImplicitCapturedArgumentNode
412412
}
413+
414+
/**
415+
* Gets a node that may execute last in `n`, and which, when it executes last,
416+
* will be the value of `n`.
417+
*/
418+
private ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
419+
exists(Expr e | any(LocalExprStepConfiguration x).hasExprPath(_, result, e, cfn) |
420+
e instanceof ConditionalExpr or
421+
e instanceof Cast or
422+
e instanceof NullCoalescingExpr or
423+
e instanceof SwitchExpr or
424+
e instanceof SuppressNullableWarningExpr or
425+
e instanceof AssignExpr
426+
)
427+
}
428+
429+
/** Gets a node for which to construct a post-update node for argument `arg`. */
430+
ControlFlow::Nodes::ExprNode getAPostUpdateNodeForArg(ControlFlow::Nodes::ExprNode arg) {
431+
arg.getExpr() instanceof Argument and
432+
result = getALastEvalNode*(arg) and
433+
exists(Expr e, Type t | result.getExpr() = e and t = e.stripCasts().getType() |
434+
t instanceof RefType and
435+
not t instanceof NullType
436+
or
437+
t = any(TypeParameter tp | not tp.isValueType())
438+
) and
439+
not exists(getALastEvalNode(result))
440+
}
413441
}
414442

415443
/**
@@ -719,14 +747,9 @@ private module Cached {
719747
cfn.getElement().(ObjectCreation).hasInitializer()
720748
} or
721749
TExprPostUpdateNode(ControlFlow::Nodes::ExprNode cfn) {
750+
cfn = LocalFlow::getAPostUpdateNodeForArg(_)
751+
or
722752
exists(Expr e | e = cfn.getExpr() |
723-
exists(Type t | t = e.(Argument).stripCasts().getType() |
724-
t instanceof RefType and
725-
not t instanceof NullType
726-
or
727-
t = any(TypeParameter tp | not tp.isValueType())
728-
)
729-
or
730753
fieldOrPropertyStore(_, _, _, e, true)
731754
or
732755
arrayStore(_, _, e, true)
@@ -1921,7 +1944,18 @@ private module PostUpdateNodes {
19211944

19221945
ExprPostUpdateNode() { this = TExprPostUpdateNode(cfn) }
19231946

1924-
override ExprNode getPreUpdateNode() { cfn = result.getControlFlowNode() }
1947+
override ExprNode getPreUpdateNode() {
1948+
// For compund arguments, such as `m(b ? x : y)`, we want the leaf nodes
1949+
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compund argument,
1950+
// `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
1951+
// respectively.
1952+
//
1953+
// This ensures that we get flow out of the call into both leafs (1), while still
1954+
// maintaining the invariant that the underlying expression is a pre-update node (2).
1955+
cfn = LocalFlow::getAPostUpdateNodeForArg(result.getControlFlowNode())
1956+
or
1957+
cfn = result.getControlFlowNode()
1958+
}
19251959

19261960
override DataFlowCallable getEnclosingCallableImpl() {
19271961
result.asCallable() = cfn.getEnclosingCallable()

csharp/ql/test/library-tests/dataflow/global/DataFlow.expected

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@
5353
| GlobalDataFlow.cs:427:41:427:46 | access to local variable sink20 |
5454
| GlobalDataFlow.cs:478:15:478:20 | access to local variable sink45 |
5555
| GlobalDataFlow.cs:486:32:486:32 | access to parameter s |
56+
| GlobalDataFlow.cs:508:15:508:22 | access to field field |
57+
| GlobalDataFlow.cs:509:15:509:22 | access to field field |
58+
| GlobalDataFlow.cs:515:15:515:22 | access to field field |
59+
| GlobalDataFlow.cs:516:15:516:22 | access to field field |
60+
| GlobalDataFlow.cs:517:15:517:22 | access to field field |
61+
| GlobalDataFlow.cs:526:15:526:21 | access to field field |
62+
| GlobalDataFlow.cs:533:15:533:21 | access to field field |
63+
| GlobalDataFlow.cs:534:15:534:21 | access to field field |
64+
| GlobalDataFlow.cs:548:15:548:21 | access to field field |
65+
| GlobalDataFlow.cs:549:15:549:21 | access to field field |
66+
| GlobalDataFlow.cs:550:15:550:21 | access to field field |
67+
| GlobalDataFlow.cs:556:15:556:22 | access to field field |
68+
| GlobalDataFlow.cs:564:15:564:21 | access to field field |
5669
| Splitting.cs:9:15:9:15 | [b (line 3): false] access to local variable x |
5770
| Splitting.cs:9:15:9:15 | [b (line 3): true] access to local variable x |
5871
| Splitting.cs:11:19:11:19 | access to local variable x |

0 commit comments

Comments
 (0)