Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5921a9e

Browse files
committed
C#: Teach guards library about assertions
1 parent 0fb7ddc commit 5921a9e

6 files changed

Lines changed: 111 additions & 15 deletions

File tree

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import csharp
66
private import ControlFlow::SuccessorTypes
7+
private import semmle.code.csharp.commons.Assertions
78
private import semmle.code.csharp.commons.ComparisonTest
89
private import semmle.code.csharp.commons.StructuralComparison::Internal
910
private import semmle.code.csharp.controlflow.BasicBlocks
@@ -472,30 +473,69 @@ module Internal {
472473
pragma [noinline]
473474
private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) {
474475
target = e.getTarget() and
475-
controls(bb, _, e, _)
476+
exists(Expr e0 |
477+
e = e0.getAChildExpr*() |
478+
controls(bb, e0, _)
479+
or
480+
controlsNode(bb.getANode(), e0, _)
481+
)
476482
}
477483
}
478484

479-
/**
480-
* Holds if basic block `bb` only is reached when `e` has abstract value `v`.
481-
* SSA qualified expression `sub` is a sub expression of `e`.
482-
*/
483-
private predicate controls(BasicBlock bb, Expr e, AccessOrCallExpr sub, AbstractValue v) {
485+
/** Holds if basic block `bb` only is reached when `e` has abstract value `v`. */
486+
private predicate controls(BasicBlock bb, Expr e, AbstractValue v) {
484487
exists(ConditionBlock cb, ConditionalSuccessor s, AbstractValue v0, Expr cond |
485488
cb.controls(bb, s) |
486489
v0.branchImplies(cb.getLastNode().getElement(), s, cond) and
487-
impliesSteps(cond, v0, e, v) and
488-
sub = e.getAChildExpr*()
490+
impliesSteps(cond, v0, e, v)
491+
)
492+
}
493+
494+
/**
495+
* Holds if assertion `a` directly asserts that expression `e` evaluates to
496+
* value `v`.
497+
*/
498+
predicate asserts(Assertion a, Expr e, AbstractValue v) {
499+
e = a.getExpr() and
500+
(
501+
a.getAssertMethod() instanceof AssertTrueMethod and
502+
v.(BooleanValue).getValue() = true
503+
or
504+
a.getAssertMethod() instanceof AssertFalseMethod and
505+
v.(BooleanValue).getValue() = false
506+
or
507+
a.getAssertMethod() instanceof AssertNullMethod and
508+
v.(NullValue).isNull()
509+
or
510+
a.getAssertMethod() instanceof AssertNonNullMethod and
511+
v = any(NullValue nv | not nv.isNull())
512+
)
513+
}
514+
515+
/** Holds if control flow node `cfn` only is reached when `e` evaluates to `v`. */
516+
private predicate controlsNode(ControlFlow::Node cfn, Expr e, AbstractValue v) {
517+
exists(Assertion a, Expr e0, AbstractValue v0 |
518+
a.getAControlFlowNode().dominates(cfn) |
519+
asserts(a, e0, v0) and
520+
impliesSteps(e0, v0, e, v)
489521
)
490522
}
491523

492524
private cached module Cached {
525+
pragma[noinline]
526+
private predicate isGuardedBy0(AccessOrCallExpr guarded, Expr e, AccessOrCallExpr sub, AbstractValue v) {
527+
exists(ControlFlow::Node cfn |
528+
(controls(cfn.getBasicBlock(), e, v) or controlsNode(cfn, e, v)) and
529+
cfn = guarded.getAControlFlowNode() and
530+
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded))
531+
)
532+
}
533+
493534
cached
494535
predicate isGuardedBy(AccessOrCallExpr guarded, Expr e, AccessOrCallExpr sub, AbstractValue v) {
495-
exists(BasicBlock bb |
496-
controls(bb, e, sub, v) and
497-
bb = guarded.getAControlFlowNode().getBasicBlock() and
498-
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded)) |
536+
isGuardedBy0(guarded, e, sub, v) and
537+
sub = e.getAChildExpr*() and
538+
(
499539
not guarded.hasSsaQualifier() and not sub.hasSsaQualifier()
500540
or
501541
guarded.getSsaQualifier() = sub.getSsaQualifier()
@@ -647,6 +687,13 @@ module Internal {
647687
e2 = e1 and
648688
v2 = v1
649689
or
690+
exists(Assertion a |
691+
e1 = a.getExpr() |
692+
asserts(a, e1, v1) and
693+
e2 = e1 and
694+
v2 = v1
695+
)
696+
or
650697
exists(Expr mid, AbstractValue vMid |
651698
impliesSteps(e1, v1, mid, vMid) |
652699
impliesStep(mid, vMid, e2, v2)

csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:22 | access to local variable s | true |
2+
| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:23:31:23 | access to local variable s | true |
3+
| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:23:38:23 | access to local variable s | true |
4+
| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:24 | access to local variable s | false |
5+
| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false |
6+
| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true |
7+
| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true |
8+
| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false |
9+
| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false |
10+
| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true |
11+
| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true |
12+
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false |
13+
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false |
114
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false |
215
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true |
316
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false |

csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1+
| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:10:22:10:22 | access to local variable s | Assert.cs:10:22:10:22 | access to local variable s | non-null |
2+
| Assert.cs:11:27:11:27 | access to local variable s | Assert.cs:10:22:10:30 | ... != ... | Assert.cs:10:22:10:22 | access to local variable s | true |
3+
| Assert.cs:18:27:18:27 | access to local variable s | Assert.cs:17:23:17:23 | access to local variable s | Assert.cs:17:23:17:23 | access to local variable s | null |
4+
| Assert.cs:25:27:25:27 | access to local variable s | Assert.cs:24:26:24:26 | access to local variable s | Assert.cs:24:26:24:26 | access to local variable s | non-null |
5+
| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:31:23:31:23 | access to local variable s | Assert.cs:31:23:31:23 | access to local variable s | null |
6+
| Assert.cs:32:27:32:27 | access to local variable s | Assert.cs:31:23:31:31 | ... == ... | Assert.cs:31:23:31:23 | access to local variable s | true |
7+
| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:38:23:38:23 | access to local variable s | Assert.cs:38:23:38:23 | access to local variable s | non-null |
8+
| Assert.cs:39:27:39:27 | access to local variable s | Assert.cs:38:23:38:31 | ... != ... | Assert.cs:38:23:38:23 | access to local variable s | true |
9+
| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:45:24:45:24 | access to local variable s | Assert.cs:45:24:45:24 | access to local variable s | null |
10+
| Assert.cs:46:27:46:27 | access to local variable s | Assert.cs:45:24:45:32 | ... != ... | Assert.cs:45:24:45:24 | access to local variable s | false |
11+
| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | Assert.cs:52:24:52:24 | access to local variable s | non-null |
12+
| Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false |
113
| Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null |
14+
| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null |
15+
| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true |
16+
| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true |
217
| Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null |
18+
| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null |
19+
| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false |
20+
| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false |
321
| Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null |
22+
| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null |
23+
| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true |
24+
| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true |
425
| Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null |
26+
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null |
27+
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false |
28+
| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false |
529
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false |
630
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true |
731
| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null |

csharp/ql/test/library-tests/controlflow/guards/NullGuardedExpr.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
| Assert.cs:11:27:11:27 | access to local variable s |
2+
| Assert.cs:25:27:25:27 | access to local variable s |
3+
| Assert.cs:39:27:39:27 | access to local variable s |
4+
| Assert.cs:53:27:53:27 | access to local variable s |
5+
| Assert.cs:60:27:60:27 | access to local variable s |
6+
| Assert.cs:67:27:67:27 | access to local variable s |
17
| Guards.cs:12:13:12:13 | access to parameter s |
28
| Guards.cs:14:31:14:31 | access to parameter s |
39
| Guards.cs:26:31:26:31 | access to parameter s |

csharp/ql/test/query-tests/Nullness/Assert.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,27 @@ void Fn()
1010
Debug.Assert(s != null);
1111
Console.WriteLine(s.Length);
1212

13+
s = null;
1314
Assert.IsNull(s);
1415
Console.WriteLine(s.Length); // always null
1516

17+
s = null;
1618
Assert.IsNotNull(s);
1719
Console.WriteLine(s.Length);
1820

21+
s = null;
1922
Assert.IsTrue(s == null);
2023
Console.WriteLine(s.Length); // always null
2124

25+
s = null;
2226
Assert.IsTrue(s != null);
2327
Console.WriteLine(s.Length);
2428

29+
s = null;
2530
Assert.IsFalse(s != null);
2631
Console.WriteLine(s.Length); // always null
2732

33+
s = null;
2834
Assert.IsFalse(s == null);
2935
Console.WriteLine(s.Length);
3036
}

csharp/ql/test/query-tests/Nullness/NullAlways.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
| A.cs:194:27:194:36 | access to local variable methodcall | Variable $@ is always null here. | A.cs:189:16:189:25 | methodcall | methodcall |
1313
| A.cs:247:31:247:44 | access to local variable eq_call_always | Variable $@ is always null here. | A.cs:241:11:241:24 | eq_call_always | eq_call_always |
1414
| A.cs:258:31:258:45 | access to local variable neq_call_always | Variable $@ is always null here. | A.cs:244:11:244:25 | neq_call_always | neq_call_always |
15-
| Assert.cs:14:27:14:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |
16-
| Assert.cs:20:27:20:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |
17-
| Assert.cs:26:27:26:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |
15+
| Assert.cs:15:27:15:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |
16+
| Assert.cs:23:27:23:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |
17+
| Assert.cs:31:27:31:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s |

0 commit comments

Comments
 (0)