@@ -24,10 +24,12 @@ private import semmle.code.csharp.commons.Assertions
2424private import semmle.code.csharp.commons.Constants
2525private import semmle.code.csharp.frameworks.System
2626private import NonReturning
27+ private import SuccessorType
28+ private import SuccessorTypes
2729
2830// Internal representation of completions
2931private newtype TCompletion =
30- TNormalCompletion ( ) or
32+ TSimpleCompletion ( ) or
3133 TBooleanCompletion ( boolean b ) { b = true or b = false } or
3234 TNullnessCompletion ( boolean isNull ) { isNull = true or isNull = false } or
3335 TMatchingCompletion ( boolean isMatch ) { isMatch = true or isMatch = false } or
@@ -78,7 +80,7 @@ private predicate completionIsValidForStmt(Stmt s, Completion c) {
7880/**
7981 * A completion of a statement or an expression.
8082 */
81- class Completion extends TCompletion {
83+ abstract class Completion extends TCompletion {
8284 /**
8385 * Holds if this completion is valid for control flow element `cfe`.
8486 *
@@ -143,7 +145,7 @@ class Completion extends TCompletion {
143145 not mustHaveNullnessCompletion ( cfe ) and
144146 not mustHaveMatchingCompletion ( cfe ) and
145147 not mustHaveEmptinessCompletion ( cfe ) and
146- this = TNormalCompletion ( )
148+ this = TSimpleCompletion ( )
147149 }
148150
149151 /**
@@ -167,8 +169,11 @@ class Completion extends TCompletion {
167169 */
168170 Completion getOuterCompletion ( ) { result = this }
169171
172+ /** Gets a successor type that matches this completion. */
173+ abstract SuccessorType getAMatchingSuccessorType ( ) ;
174+
170175 /** Gets a textual representation of this completion. */
171- string toString ( ) { none ( ) }
176+ abstract string toString ( ) ;
172177}
173178
174179/** Holds if expression `e` has the Boolean constant value `value`. */
@@ -529,10 +534,10 @@ private predicate mustHaveEmptinessCompletion(ControlFlowElement cfe) { foreachE
529534 */
530535abstract class NormalCompletion extends Completion { }
531536
532- /**
533- * A class to make `TNormalCompletion` a `NormalCompletion`
534- */
535- class SimpleCompletion extends NormalCompletion , TNormalCompletion {
537+ /** A simple (normal) completion. */
538+ class SimpleCompletion extends NormalCompletion , TSimpleCompletion {
539+ override NormalSuccessor getAMatchingSuccessorType ( ) { any ( ) }
540+
536541 override string toString ( ) { result = "normal" }
537542}
538543
@@ -558,6 +563,8 @@ class BooleanCompletion extends ConditionalCompletion {
558563
559564 BooleanCompletion getDual ( ) { result = TBooleanCompletion ( value .booleanNot ( ) ) }
560565
566+ override BooleanSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
567+
561568 override string toString ( ) { result = value .toString ( ) }
562569}
563570
@@ -576,11 +583,17 @@ class FalseCompletion extends BooleanCompletion {
576583 * `null` or non-`null`.
577584 */
578585class NullnessCompletion extends ConditionalCompletion , TNullnessCompletion {
586+ private boolean value ;
587+
588+ NullnessCompletion ( ) { this = TNullnessCompletion ( value ) }
589+
579590 /** Holds if the last sub expression of this expression evaluates to `null`. */
580- predicate isNull ( ) { this = TNullnessCompletion ( true ) }
591+ predicate isNull ( ) { value = true }
581592
582593 /** Holds if the last sub expression of this expression evaluates to a non-`null` value. */
583- predicate isNonNull ( ) { this = TNullnessCompletion ( false ) }
594+ predicate isNonNull ( ) { value = false }
595+
596+ override NullnessSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
584597
585598 override string toString ( ) { if this .isNull ( ) then result = "null" else result = "non-null" }
586599}
@@ -590,11 +603,17 @@ class NullnessCompletion extends ConditionalCompletion, TNullnessCompletion {
590603 * `switch` statement.
591604 */
592605class MatchingCompletion extends ConditionalCompletion , TMatchingCompletion {
606+ private boolean value ;
607+
608+ MatchingCompletion ( ) { this = TMatchingCompletion ( value ) }
609+
593610 /** Holds if there is a match. */
594- predicate isMatch ( ) { this = TMatchingCompletion ( true ) }
611+ predicate isMatch ( ) { value = true }
595612
596613 /** Holds if there is not a match. */
597- predicate isNonMatch ( ) { this = TMatchingCompletion ( false ) }
614+ predicate isNonMatch ( ) { value = false }
615+
616+ override MatchingSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
598617
599618 override string toString ( ) { if this .isMatch ( ) then result = "match" else result = "no-match" }
600619}
@@ -604,8 +623,14 @@ class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
604623 * a test in a `foreach` statement.
605624 */
606625class EmptinessCompletion extends ConditionalCompletion , TEmptinessCompletion {
626+ private boolean value ;
627+
628+ EmptinessCompletion ( ) { this = TEmptinessCompletion ( value ) }
629+
607630 /** Holds if the emptiness test evaluates to `true`. */
608- predicate isEmpty ( ) { this = TEmptinessCompletion ( true ) }
631+ predicate isEmpty ( ) { value = true }
632+
633+ override EmptinessSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
609634
610635 override string toString ( ) { if this .isEmpty ( ) then result = "empty" else result = "non-empty" }
611636}
@@ -616,7 +641,7 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
616641 *
617642 * This completion is added for technical reasons only: when a loop
618643 * body can complete with a break completion, the loop itself completes
619- * normally. However, if we choose `TNormalCompletion ` as the completion
644+ * normally. However, if we choose `TSimpleCompletion ` as the completion
620645 * of the loop, we lose the information that the last element actually
621646 * completed with a break, meaning that the control flow edge out of the
622647 * breaking node cannot be marked with a `break` label.
@@ -634,10 +659,12 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
634659 * The `break` on line 3 completes with a `TBreakCompletion`, therefore
635660 * the `while` loop can complete with a `TBreakNormalCompletion`, so we
636661 * get an edge `break --break--> return`. (If we instead used a
637- * `TNormalCompletion `, we would get a less precise edge
662+ * `TSimpleCompletion `, we would get a less precise edge
638663 * `break --normal--> return`.)
639664 */
640665class BreakNormalCompletion extends NormalCompletion , TBreakNormalCompletion {
666+ override BreakSuccessor getAMatchingSuccessorType ( ) { any ( ) }
667+
641668 override string toString ( ) { result = "normal (break)" }
642669}
643670
@@ -673,6 +700,8 @@ class NestedCompletion extends Completion, TNestedCompletion {
673700
674701 override Completion getOuterCompletion ( ) { result = outer }
675702
703+ override SuccessorType getAMatchingSuccessorType ( ) { none ( ) }
704+
676705 override string toString ( ) { result = outer + " [" + inner + "]" }
677706}
678707
@@ -686,6 +715,8 @@ class ReturnCompletion extends Completion {
686715 this = TNestedCompletion ( _, TReturnCompletion ( ) )
687716 }
688717
718+ override ReturnSuccessor getAMatchingSuccessorType ( ) { any ( ) }
719+
689720 override string toString ( ) {
690721 // `NestedCompletion` defines `toString()` for the other case
691722 this = TReturnCompletion ( ) and result = "return"
@@ -703,6 +734,8 @@ class BreakCompletion extends Completion {
703734 this = TNestedCompletion ( _, TBreakCompletion ( ) )
704735 }
705736
737+ override BreakSuccessor getAMatchingSuccessorType ( ) { any ( ) }
738+
706739 override string toString ( ) {
707740 // `NestedCompletion` defines `toString()` for the other case
708741 this = TBreakCompletion ( ) and result = "break"
@@ -720,6 +753,8 @@ class ContinueCompletion extends Completion {
720753 this = TNestedCompletion ( _, TContinueCompletion ( ) )
721754 }
722755
756+ override ContinueSuccessor getAMatchingSuccessorType ( ) { any ( ) }
757+
723758 override string toString ( ) {
724759 // `NestedCompletion` defines `toString()` for the other case
725760 this = TContinueCompletion ( ) and result = "continue"
@@ -741,6 +776,8 @@ class GotoCompletion extends Completion {
741776 /** Gets the label of the `goto` completion. */
742777 string getLabel ( ) { result = label }
743778
779+ override GotoSuccessor getAMatchingSuccessorType ( ) { result .getLabel ( ) = label }
780+
744781 override string toString ( ) {
745782 // `NestedCompletion` defines `toString()` for the other case
746783 this = TGotoCompletion ( label ) and result = "goto(" + label + ")"
@@ -762,6 +799,8 @@ class ThrowCompletion extends Completion {
762799 /** Gets the type of the exception being thrown. */
763800 ExceptionClass getExceptionClass ( ) { result = ec }
764801
802+ override ExceptionSuccessor getAMatchingSuccessorType ( ) { result .getExceptionClass ( ) = ec }
803+
765804 override string toString ( ) {
766805 // `NestedCompletion` defines `toString()` for the other case
767806 this = TThrowCompletion ( ec ) and result = "throw(" + ec + ")"
@@ -783,6 +822,8 @@ class ExitCompletion extends Completion {
783822 this = TNestedCompletion ( _, TExitCompletion ( ) )
784823 }
785824
825+ override ExitSuccessor getAMatchingSuccessorType ( ) { any ( ) }
826+
786827 override string toString ( ) {
787828 // `NestedCompletion` defines `toString()` for the other case
788829 this = TExitCompletion ( ) and result = "exit"
0 commit comments