Skip to content

Commit b1ef638

Browse files
paldepindCopilot
andcommitted
Shared: Support exhaustive switches in the shared control-flow graph
Add an `isExhaustive` input hook and treat the final case of an exhaustive switch as match-all. Default to non-exhaustive to preserve existing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4a9b52f8-d07b-4692-8fb9-409f0efd64f2
1 parent 2a4cb24 commit b1ef638

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

‎shared/controlflow/codeql/controlflow/ControlFlowGraph.qll‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,14 +538,19 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
538538
/** Holds if catch clause `catch` catches all exceptions. */
539539
default predicate catchAll(CatchClause catch) { not exists(catch.getPattern()) }
540540

541+
/** Holds if `switch` is exhaustive, so one of its cases must match. */
542+
default predicate isExhaustive(Switch switch) { none() }
543+
541544
/**
542545
* Holds if case `c` matches all possible values, for example, if it is a
543-
* `default` case or a match-all pattern like `Object o` or if it is the
544-
* final case in a switch that is known to be exhaustive.
546+
* match-all pattern like `Object o`.
545547
*
546548
* A match-all case can still ultimately fail to match if it has a guard.
549+
*
550+
* Default cases are handled automatically. The final case in an exhaustive
551+
* switch should not be handled here but through `isExhaustive`.
547552
*/
548-
default predicate matchAll(Case c) { c instanceof DefaultCase }
553+
default predicate matchAll(Case c) { none() }
549554
}
550555

551556
/**
@@ -847,11 +852,23 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
847852
result = DenseRank2<ParameterCtxDenseRankInput>::denseRank(c, ctx.asSome(), rnk)
848853
}
849854

855+
private predicate matchAll(Case c) {
856+
c instanceof DefaultCase
857+
or
858+
Input1::matchAll(c)
859+
or
860+
exists(Switch switch, int last |
861+
Input1::isExhaustive(switch) and
862+
switch.getCase(last) = c and
863+
not exists(switch.getCase(last + 1))
864+
)
865+
}
866+
850867
private predicate constantCondition(AstNode n, ConditionalSuccessor t) {
851868
n.(BooleanLiteral).getValue() = t.(BooleanSuccessor).getValue()
852869
or
853870
exists(Case c, int i |
854-
Input1::matchAll(c) and
871+
matchAll(c) and
855872
c.getPattern(i) = n and
856873
not exists(c.getPattern(i + 1)) and
857874
t.(MatchingSuccessor).getValue() = true

0 commit comments

Comments
 (0)