From fa8c1d62264fc11c6f63c88a10150b629e8c5bd4 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 28 Apr 2026 15:44:12 +0200 Subject: [PATCH 1/4] C++: Add a `getSwitchCase` predicate to `SwitchStmt` --- .../change-notes/2026-03-28-switch-stmt.md | 4 ++++ cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2026-03-28-switch-stmt.md diff --git a/cpp/ql/lib/change-notes/2026-03-28-switch-stmt.md b/cpp/ql/lib/change-notes/2026-03-28-switch-stmt.md new file mode 100644 index 000000000000..4b0d7528d479 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-03-28-switch-stmt.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* A new predicate `getSwitchCase` was added to the `SwitchStmt` class, which yields the `n`th `case` statement from a `switch` statement. diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index cd7504612444..c974353bc7a1 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -1827,6 +1827,26 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { */ SwitchCase getASwitchCase() { switch_case(underlyingElement(this), _, unresolveElement(result)) } + /** + * Gets the `n`th 'switch case' statement of this 'switch' statement, where + * `n` is 0-based. + * + * For example, for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * 0 yields `case 5:`, 1 yields `case 6:`, and 2 yields `default:`. + */ + SwitchCase getSwitchCase(int n) { + switch_case(underlyingElement(this), n, unresolveElement(result)) + } + /** * Gets the 'default case' statement of this 'switch' statement, * if any. From f634b328eeba3ea359dc52b1d570f458182fc875 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 28 Apr 2026 15:44:53 +0200 Subject: [PATCH 2/4] C++: Fix join-order problem in `getNextSwitchCase` Before on `neovim`: ``` [2026-04-28 14:54:20] Evaluated non-recursive predicate Stmt::SwitchCase.getNextSwitchCase/0#dispred#2d3cb6d3@ac8178o2 in 68ms (size: 20848). Evaluated relational algebra for predicate Stmt::SwitchCase.getNextSwitchCase/0#dispred#2d3cb6d3@ac8178o2 with tuple counts: 21888 ~0% {2} r1 = SCAN switch_case OUTPUT In.2, In.0 21888 ~0% {4} | JOIN WITH #switch_caseMerge_21#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0, _, Rhs.1 21888 ~4% {3} | REWRITE WITH Tmp.2 := 1, Out.2 := (In.3 - Tmp.2) KEEPING 3 24091916 ~0% {3} | JOIN WITH switch_case ON FIRST 1 OUTPUT Lhs.2, Rhs.2, Lhs.1 20848 ~2% {2} | JOIN WITH #switch_caseMerge_12#join_rhs ON FIRST 2 OUTPUT Lhs.1, Lhs.2 return r1 ``` After: ``` [2026-04-28 15:30:53] Evaluated non-recursive predicate Stmt::SwitchCase.getNextSwitchCase/0#dispred#2d3cb6d3@bf9801oj in 0ms (size: 20848). Evaluated relational algebra for predicate Stmt::SwitchCase.getNextSwitchCase/0#dispred#2d3cb6d3@bf9801oj with tuple counts: 21888 ~0% {4} r1 = SCAN switch_case OUTPUT In.0, _, In.2, In.1 21888 ~1% {3} | REWRITE WITH Tmp.1 := 1, Out.1 := (In.3 + Tmp.1) KEEPING 3 20848 ~2% {2} | JOIN WITH switch_case ON FIRST 2 OUTPUT Lhs.2, Rhs.2 return r1 ``` --- cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index c974353bc7a1..b7905a9d23fc 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -1516,8 +1516,10 @@ class SwitchCase extends Stmt, @stmt_switch_case { * which has result `default:`, which has no result. */ SwitchCase getNextSwitchCase() { - result.getSwitchStmt() = this.getSwitchStmt() and - result.getChildNum() = this.getChildNum() + 1 + exists(SwitchStmt s, int n | + this = s.getSwitchCase(n) and + result = s.getSwitchCase(n + 1) + ) } /** From 0bc23c3af1b7bb5a374e3988ae30476717607346 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 28 Apr 2026 16:33:17 +0200 Subject: [PATCH 3/4] C++: Match example with text --- cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index b7905a9d23fc..c4bd6088d26e 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -1836,13 +1836,10 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, for * ``` * switch(i) { - * case 1: - * case 2: - * break; - * default: - * break; - * } - * ``` + * case 5: + * case 6: + * default: + * } * ``` * 0 yields `case 5:`, 1 yields `case 6:`, and 2 yields `default:`. */ SwitchCase getSwitchCase(int n) { From 29dd56f83fc3ad4965a446a8e873ea583822a005 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 28 Apr 2026 16:36:54 +0200 Subject: [PATCH 4/4] C++: Make formatting of `switch` statement examples more uniform --- cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index c4bd6088d26e..ccda6c4d592d 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -1412,9 +1412,9 @@ private int indexOfSwitchCaseRank(BlockStmt b, int rnk) { * switch (i) * { * case 5: - * ... + * ... * default: - * ... + * ... * } * ``` */ @@ -1709,9 +1709,9 @@ class SwitchCase extends Stmt, @stmt_switch_case { * switch (i) * { * case 5: - * ... + * ... * default: - * ... + * ... * } * ``` */ @@ -1733,9 +1733,9 @@ class DefaultCase extends SwitchCase { * switch (i) * { * case 5: - * ... + * ... * default: - * ... + * ... * } * ``` */ @@ -1770,10 +1770,10 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` @@ -1792,20 +1792,20 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` * the result is * ``` * { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` @@ -1818,10 +1818,10 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` @@ -1853,18 +1853,18 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` * the result is `default:`, but there is no result for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; * } * ``` @@ -1877,18 +1877,18 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch { * For example, this holds for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; - * default: + * default: * break; * } * ``` * but not for * ``` * switch(i) { - * case 1: - * case 2: + * case 1: + * case 2: * break; * } * ```