@@ -19,7 +19,8 @@ private newtype TCompletion =
1919 TContinueCompletion ( ) or
2020 TThrowCompletion ( ) or
2121 TExitCompletion ( ) or
22- TMatchingCompletion ( Boolean b )
22+ TMatchingCompletion ( Boolean b ) or
23+ TEmptinessCompletion ( Boolean isEmpty )
2324
2425private predicate commandThrows ( Cmd c , boolean unconditional ) {
2526 c .getNamedArgument ( "ErrorAction" ) .( StringConstExpr ) .getValue ( ) .getValue ( ) = "Stop" and
@@ -68,6 +69,9 @@ abstract class Completion extends TCompletion {
6869 not isMatchingConstant ( n , _) and
6970 this = TMatchingCompletion ( _)
7071 )
72+ or
73+ mustHaveEmptinessCompletion ( n ) and
74+ this = TEmptinessCompletion ( _)
7175 }
7276
7377 private predicate isValidForSpecific ( Ast n ) { this .isValidForSpecific0 ( n ) }
@@ -183,6 +187,12 @@ private predicate inMatchingContext(Ast n) {
183187 n = any ( CatchClause cc ) .getACatchType ( )
184188}
185189
190+ /**
191+ * Holds if a normal completion of `cfe` must be an emptiness completion. Thats is,
192+ * whether `cfe` determines whether to execute the body of a `foreach` statement.
193+ */
194+ private predicate mustHaveEmptinessCompletion ( Ast n ) { n instanceof ForEachStmt }
195+
186196/**
187197 * A completion that represents normal evaluation of a statement or an
188198 * expression.
@@ -296,3 +306,20 @@ class ExitCompletion extends Completion, TExitCompletion {
296306
297307 override string toString ( ) { result = "exit" }
298308}
309+
310+ /**
311+ * A completion that represents evaluation of an emptiness test, for example
312+ * a test in a `foreach` statement.
313+ */
314+ class EmptinessCompletion extends ConditionalCompletion , TEmptinessCompletion {
315+ EmptinessCompletion ( ) { this = TEmptinessCompletion ( value ) }
316+
317+ /** Holds if the emptiness test evaluates to `true`. */
318+ predicate isEmpty ( ) { value = true }
319+
320+ EmptinessCompletion getDual ( ) { result = TEmptinessCompletion ( value .booleanNot ( ) ) }
321+
322+ override EmptinessSuccessor getAMatchingSuccessorType ( ) { result .getValue ( ) = value }
323+
324+ override string toString ( ) { if this .isEmpty ( ) then result = "empty" else result = "non-empty" }
325+ }
0 commit comments