Skip to content

Commit 94deed3

Browse files
committed
C#: Represent all expressions in post-order in the CFG
1 parent 202f7f0 commit 94deed3

38 files changed

+3486
-3106
lines changed

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

Lines changed: 148 additions & 170 deletions
Large diffs are not rendered by default.

csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll

Lines changed: 44 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private newtype TCompletion =
3939
TGotoCompletion(string label) { label = any(GotoStmt gs).getLabel() } or
4040
TThrowCompletion(ExceptionClass ec) or
4141
TExitCompletion() or
42-
TNestedCompletion(NormalCompletion inner, Completion outer) {
42+
TNestedCompletion(ConditionalCompletion inner, Completion outer) {
4343
outer = TReturnCompletion()
4444
or
4545
outer = TBreakCompletion()
@@ -51,8 +51,6 @@ private newtype TCompletion =
5151
outer = TThrowCompletion(_)
5252
or
5353
outer = TExitCompletion()
54-
or
55-
exists(boolean b | inner = TBooleanCompletion(b) and outer = TBooleanCompletion(b.booleanNot()))
5654
}
5755

5856
pragma[noinline]
@@ -407,126 +405,87 @@ Completion assertionCompletion(Assertion a, int i) {
407405
* Holds if a normal completion of `e` must be a Boolean completion.
408406
*/
409407
private predicate mustHaveBooleanCompletion(Expr e) {
410-
inBooleanContext(e, _) and
411-
not inBooleanContext(e.getAChildExpr(), true) and
408+
inBooleanContext(e) and
412409
not e instanceof NonReturningCall
413410
}
414411

415412
/**
416413
* Holds if `e` is used in a Boolean context. That is, whether the value
417414
* that `e` evaluates to determines a true/false branch successor.
418-
*
419-
* `isBooleanCompletionForParent` indicates whether the Boolean completion
420-
* for `e` will be the Boolean completion for `e`'s parent. For example,
421-
* if `e = B` and the parent is `A && B`, then the Boolean completion of
422-
* `B` is the Boolean completion of `A && B`.
423415
*/
424-
private predicate inBooleanContext(Expr e, boolean isBooleanCompletionForParent) {
425-
exists(IfStmt is | is.getCondition() = e | isBooleanCompletionForParent = false)
416+
private predicate inBooleanContext(Expr e) {
417+
e = any(IfStmt is).getCondition()
426418
or
427-
exists(LoopStmt ls | ls.getCondition() = e | isBooleanCompletionForParent = false)
419+
e = any(LoopStmt ls).getCondition()
428420
or
429-
exists(Case c | c.getCondition() = e | isBooleanCompletionForParent = false)
421+
e = any(Case c).getCondition()
430422
or
431-
exists(SpecificCatchClause scc | scc.getFilterClause() = e | isBooleanCompletionForParent = false)
423+
e = any(SpecificCatchClause scc).getFilterClause()
432424
or
433425
exists(BooleanAssertMethod m, int i |
434426
assertion(_, i, m, e) and
435-
i = m.getAnAssertionIndex(_) and
436-
isBooleanCompletionForParent = false
427+
i = m.getAnAssertionIndex(_)
437428
)
438429
or
439-
exists(LogicalNotExpr lne | lne.getAnOperand() = e |
440-
inBooleanContext(lne, _) and
441-
isBooleanCompletionForParent = true
442-
)
430+
e = any(LogicalNotExpr lne | inBooleanContext(lne)).getAnOperand()
443431
or
444432
exists(LogicalAndExpr lae |
445-
lae.getLeftOperand() = e and
446-
isBooleanCompletionForParent = false
433+
lae.getLeftOperand() = e
447434
or
448-
lae.getRightOperand() = e and
449-
inBooleanContext(lae, _) and
450-
isBooleanCompletionForParent = true
435+
inBooleanContext(lae) and
436+
lae.getRightOperand() = e
451437
)
452438
or
453439
exists(LogicalOrExpr lae |
454-
lae.getLeftOperand() = e and
455-
isBooleanCompletionForParent = false
440+
lae.getLeftOperand() = e
456441
or
457-
lae.getRightOperand() = e and
458-
inBooleanContext(lae, _) and
459-
isBooleanCompletionForParent = true
442+
inBooleanContext(lae) and
443+
lae.getRightOperand() = e
460444
)
461445
or
462446
exists(ConditionalExpr ce |
463-
ce.getCondition() = e and
464-
isBooleanCompletionForParent = false
447+
ce.getCondition() = e
465448
or
466-
(ce.getThen() = e or ce.getElse() = e) and
467-
inBooleanContext(ce, _) and
468-
isBooleanCompletionForParent = true
449+
inBooleanContext(ce) and
450+
e in [ce.getThen(), ce.getElse()]
469451
)
470452
or
471-
exists(NullCoalescingExpr nce | nce.getAnOperand() = e |
472-
inBooleanContext(nce, _) and
473-
isBooleanCompletionForParent = true
474-
)
453+
e = any(NullCoalescingExpr nce | inBooleanContext(nce)).getAnOperand()
475454
or
476-
exists(SwitchExpr se |
477-
inBooleanContext(se, _) and
478-
e = se.getACase().getBody() and
479-
isBooleanCompletionForParent = true
480-
)
455+
e = any(SwitchExpr se | inBooleanContext(se)).getACase()
456+
or
457+
e = any(SwitchCaseExpr sce | inBooleanContext(sce)).getBody()
481458
}
482459

483460
/**
484461
* Holds if a normal completion of `e` must be a nullness completion.
485462
*/
486463
private predicate mustHaveNullnessCompletion(Expr e) {
487-
inNullnessContext(e, _) and
488-
not inNullnessContext(e.getAChildExpr(), true) and
464+
inNullnessContext(e) and
489465
not e instanceof NonReturningCall
490466
}
491467

492468
/**
493469
* Holds if `e` is used in a nullness context. That is, whether the value
494470
* that `e` evaluates to determines a `null`/non-`null` branch successor.
495-
*
496-
* `isNullnessCompletionForParent` indicates whether the nullness completion
497-
* for `e` will be the nullness completion for `e`'s parent. For example,
498-
* if `e = A` and the parent is `A ?? B`, then the nullness completion of `B`
499-
* is the nullness completion of `A ?? B`.
500471
*/
501-
private predicate inNullnessContext(Expr e, boolean isNullnessCompletionForParent) {
502-
exists(NullCoalescingExpr nce | e = nce.getLeftOperand() | isNullnessCompletionForParent = false)
472+
private predicate inNullnessContext(Expr e) {
473+
e = any(NullCoalescingExpr nce).getLeftOperand()
503474
or
504-
exists(QualifiableExpr qe | qe.isConditional() |
505-
e = qe.getChildExpr(-1) and
506-
isNullnessCompletionForParent = false
507-
)
475+
exists(QualifiableExpr qe | qe.isConditional() | e = qe.getChildExpr(-1))
508476
or
509477
exists(NullnessAssertMethod m, int i |
510478
assertion(_, i, m, e) and
511-
i = m.getAnAssertionIndex(_) and
512-
isNullnessCompletionForParent = false
479+
i = m.getAnAssertionIndex(_)
513480
)
514481
or
515-
exists(ConditionalExpr ce | inNullnessContext(ce, _) |
516-
(e = ce.getThen() or e = ce.getElse()) and
517-
isNullnessCompletionForParent = true
518-
)
482+
exists(ConditionalExpr ce | inNullnessContext(ce) | (e = ce.getThen() or e = ce.getElse()))
519483
or
520-
exists(NullCoalescingExpr nce | inNullnessContext(nce, _) |
521-
e = nce.getRightOperand() and
522-
isNullnessCompletionForParent = true
523-
)
484+
exists(NullCoalescingExpr nce | inNullnessContext(nce) | e = nce.getRightOperand())
524485
or
525-
exists(SwitchExpr se |
526-
inNullnessContext(se, _) and
527-
e = se.getACase().getBody() and
528-
isNullnessCompletionForParent = true
529-
)
486+
e = any(SwitchExpr se | inNullnessContext(se)).getACase()
487+
or
488+
e = any(SwitchCaseExpr sce | inNullnessContext(sce)).getBody()
530489
}
531490

532491
/**
@@ -592,18 +551,14 @@ abstract class ConditionalCompletion extends NormalCompletion { }
592551
class BooleanCompletion extends ConditionalCompletion {
593552
private boolean value;
594553

595-
BooleanCompletion() {
596-
this = TBooleanCompletion(value) or
597-
this = TNestedCompletion(_, TBooleanCompletion(value))
598-
}
554+
BooleanCompletion() { this = TBooleanCompletion(value) }
599555

600556
/** Gets the Boolean value of this completion. */
601557
boolean getValue() { result = value }
602558

603-
override string toString() {
604-
this = TBooleanCompletion(value) and
605-
result = this.getValue().toString()
606-
}
559+
BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) }
560+
561+
override string toString() { result = value.toString() }
607562
}
608563

609564
/** A Boolean `true` completion. */
@@ -690,30 +645,31 @@ class BreakNormalCompletion extends NormalCompletion, TBreakNormalCompletion {
690645
* A nested completion. For example, in
691646
*
692647
* ```csharp
693-
* void M(bool b)
648+
* void M(bool b1, bool b2)
694649
* {
695650
* try
696651
* {
697-
* if (b)
652+
* if (b1)
698653
* throw new Exception();
699654
* }
700655
* finally
701656
* {
702-
* System.Console.WriteLine("M called");
657+
* if (b2)
658+
* System.Console.WriteLine("M called");
703659
* }
704660
* }
705661
* ```
706662
*
707-
* `System.Console.WriteLine("M called")` has an outer throw completion
708-
* from `throw new Exception` and an inner simple completion.
663+
* `b2` has an outer throw completion (inherited from `throw new Exception`)
664+
* and an inner `false` completion. `b2` also has a (normal) `true` completion.
709665
*/
710666
class NestedCompletion extends Completion, TNestedCompletion {
711-
private NormalCompletion inner;
667+
private ConditionalCompletion inner;
712668
private Completion outer;
713669

714670
NestedCompletion() { this = TNestedCompletion(inner, outer) }
715671

716-
override NormalCompletion getInnerCompletion() { result = inner }
672+
override ConditionalCompletion getInnerCompletion() { result = inner }
717673

718674
override Completion getOuterCompletion() { result = outer }
719675

0 commit comments

Comments
 (0)