@@ -36,23 +36,28 @@ private newtype TCompletion =
3636 TEmptinessCompletion ( boolean isEmpty ) { isEmpty = true or isEmpty = false } or
3737 TReturnCompletion ( ) or
3838 TBreakCompletion ( ) or
39- TBreakNormalCompletion ( ) or
4039 TContinueCompletion ( ) or
4140 TGotoCompletion ( string label ) { label = any ( GotoStmt gs ) .getLabel ( ) } or
4241 TThrowCompletion ( ExceptionClass ec ) or
4342 TExitCompletion ( ) or
44- TNestedCompletion ( ConditionalCompletion inner , Completion outer ) {
45- outer = TReturnCompletion ( )
46- or
47- outer = TBreakCompletion ( )
48- or
49- outer = TContinueCompletion ( )
50- or
51- outer = TGotoCompletion ( _)
52- or
53- outer = TThrowCompletion ( _)
43+ TNestedCompletion ( Completion inner , Completion outer ) {
44+ inner instanceof NormalCompletion and
45+ (
46+ outer = TReturnCompletion ( )
47+ or
48+ outer = TBreakCompletion ( )
49+ or
50+ outer = TContinueCompletion ( )
51+ or
52+ outer = TGotoCompletion ( _)
53+ or
54+ outer = TThrowCompletion ( _)
55+ or
56+ outer = TExitCompletion ( )
57+ )
5458 or
55- outer = TExitCompletion ( )
59+ inner = TBreakCompletion ( ) and
60+ outer instanceof NonNestedNormalCompletion
5661 }
5762
5863pragma [ noinline]
@@ -534,8 +539,10 @@ private predicate mustHaveEmptinessCompletion(ControlFlowElement cfe) { foreachE
534539 */
535540abstract class NormalCompletion extends Completion { }
536541
542+ abstract private class NonNestedNormalCompletion extends NormalCompletion { }
543+
537544/** A simple (normal) completion. */
538- class SimpleCompletion extends NormalCompletion , TSimpleCompletion {
545+ class SimpleCompletion extends NonNestedNormalCompletion , TSimpleCompletion {
539546 override NormalSuccessor getAMatchingSuccessorType ( ) { any ( ) }
540547
541548 override string toString ( ) { result = "normal" }
@@ -547,7 +554,7 @@ class SimpleCompletion extends NormalCompletion, TSimpleCompletion {
547554 * completion (`NullnessCompletion`), a matching completion (`MatchingCompletion`),
548555 * or an emptiness completion (`EmptinessCompletion`).
549556 */
550- abstract class ConditionalCompletion extends NormalCompletion { }
557+ abstract class ConditionalCompletion extends NonNestedNormalCompletion { }
551558
552559/**
553560 * A completion that represents evaluation of an expression
@@ -635,39 +642,6 @@ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
635642 override string toString ( ) { if this .isEmpty ( ) then result = "empty" else result = "non-empty" }
636643}
637644
638- /**
639- * A completion that represents evaluation of a statement or
640- * expression resulting in a loop break.
641- *
642- * This completion is added for technical reasons only: when a loop
643- * body can complete with a break completion, the loop itself completes
644- * normally. However, if we choose `TSimpleCompletion` as the completion
645- * of the loop, we lose the information that the last element actually
646- * completed with a break, meaning that the control flow edge out of the
647- * breaking node cannot be marked with a `break` label.
648- *
649- * Example:
650- *
651- * ```csharp
652- * while (...) {
653- * ...
654- * break;
655- * }
656- * return;
657- * ```
658- *
659- * The `break` on line 3 completes with a `TBreakCompletion`, therefore
660- * the `while` loop can complete with a `TBreakNormalCompletion`, so we
661- * get an edge `break --break--> return`. (If we instead used a
662- * `TSimpleCompletion`, we would get a less precise edge
663- * `break --normal--> return`.)
664- */
665- class BreakNormalCompletion extends NormalCompletion , TBreakNormalCompletion {
666- override BreakSuccessor getAMatchingSuccessorType ( ) { any ( ) }
667-
668- override string toString ( ) { result = "normal (break)" }
669- }
670-
671645/**
672646 * A nested completion. For example, in
673647 *
@@ -691,12 +665,17 @@ class BreakNormalCompletion extends NormalCompletion, TBreakNormalCompletion {
691665 * and an inner `false` completion. `b2` also has a (normal) `true` completion.
692666 */
693667class NestedCompletion extends Completion , TNestedCompletion {
694- private ConditionalCompletion inner ;
695- private Completion outer ;
668+ Completion inner ;
669+ Completion outer ;
696670
697671 NestedCompletion ( ) { this = TNestedCompletion ( inner , outer ) }
698672
699- override ConditionalCompletion getInnerCompletion ( ) { result = inner }
673+ /** Gets a completion that is compatible with the inner completion. */
674+ Completion getAnInnerCompatibleCompletion ( ) {
675+ result .getOuterCompletion ( ) = this .getInnerCompletion ( )
676+ }
677+
678+ override Completion getInnerCompletion ( ) { result = inner }
700679
701680 override Completion getOuterCompletion ( ) { result = outer }
702681
@@ -705,6 +684,57 @@ class NestedCompletion extends Completion, TNestedCompletion {
705684 override string toString ( ) { result = outer + " [" + inner + "]" }
706685}
707686
687+ /**
688+ * A nested completion for a loop that exists with a `break`.
689+ *
690+ * This completion is added for technical reasons only: when a loop
691+ * body can complete with a break completion, the loop itself completes
692+ * normally. However, if we choose `TSimpleCompletion` as the completion
693+ * of the loop, we lose the information that the last element actually
694+ * completed with a break, meaning that the control flow edge out of the
695+ * breaking node cannot be marked with a `break` label.
696+ *
697+ * Example:
698+ *
699+ * ```csharp
700+ * while (...) {
701+ * ...
702+ * break;
703+ * }
704+ * return;
705+ * ```
706+ *
707+ * The `break` on line 3 completes with a `TBreakCompletion`, therefore
708+ * the `while` loop can complete with a `NestedBreakCompletion`, so we
709+ * get an edge `break --break--> return`. (If we instead used a
710+ * `TSimpleCompletion`, we would get a less precise edge
711+ * `break --normal--> return`.)
712+ */
713+ class NestedBreakCompletion extends NormalCompletion , NestedCompletion {
714+ NestedBreakCompletion ( ) {
715+ inner = TBreakCompletion ( ) and
716+ outer instanceof NonNestedNormalCompletion
717+ }
718+
719+ override BreakCompletion getInnerCompletion ( ) { result = inner }
720+
721+ override SimpleCompletion getOuterCompletion ( ) { result = outer }
722+
723+ override Completion getAnInnerCompatibleCompletion ( ) {
724+ result = inner and
725+ outer = TSimpleCompletion ( )
726+ or
727+ result = TNestedCompletion ( outer , inner )
728+ }
729+
730+ override SuccessorType getAMatchingSuccessorType ( ) {
731+ outer instanceof SimpleCompletion and
732+ result instanceof BreakSuccessor
733+ or
734+ result = outer .( ConditionalCompletion ) .getAMatchingSuccessorType ( )
735+ }
736+ }
737+
708738/**
709739 * A completion that represents evaluation of a statement or an
710740 * expression resulting in a return from a callable.
0 commit comments