Skip to content

Fix switch default clause not participating in fallthrough#47

Merged
frostney merged 1 commit into
mainfrom
fix/switch-default-fallthrough
Mar 8, 2026
Merged

Fix switch default clause not participating in fallthrough#47
frostney merged 1 commit into
mainfrom
fix/switch-default-fallthrough

Conversation

@frostney
Copy link
Copy Markdown
Owner

@frostney frostney commented Mar 8, 2026

When a prior case matched and fell through, the default clause body was unconditionally skipped because the first-pass loop used Continue for every default clause regardless of match state. Now the default clause is only skipped when no match has been found yet; once Matched = True, it falls through like any other clause.

Fixes #46

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed switch statement default case handling to correctly evaluate subsequent logic after a match has been found.
  • Tests

    • Added comprehensive test coverage for switch statement fall-through behavior with default cases, including break statement interactions.

When a prior case matched and fell through, the default clause body
was unconditionally skipped because the first-pass loop used Continue
for every default clause regardless of match state. Now the default
clause is only skipped when no match has been found yet; once
Matched = True, it falls through like any other clause.

Fixes #46

Co-authored-by: Johannes Stein <frostney@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e48d7b1-d0dc-4bf3-8c47-d4d1d8b76e00

📥 Commits

Reviewing files that changed from the base of the PR and between 34d54dd and 4d74330.

📒 Files selected for processing (2)
  • tests/language/statements/switch/switch-edge-cases.js
  • units/Goccia.Evaluator.pas

📝 Walkthrough

Walkthrough

A bug fix enabling the default clause to participate in switch statement fallthrough after a matched case, paired with comprehensive test cases validating various fallthrough scenarios and the interaction between cases, defaults, and break statements.

Changes

Cohort / File(s) Summary
Switch Fallthrough Tests
tests/language/statements/switch/switch-edge-cases.js
Adds 75 lines of test cases exploring switch fallthrough behavior: matched case falling through into/past default, default unreached with break, default executing when no case matches, and default in middle positions with matches before/after.
Evaluator Logic
units/Goccia.Evaluator.pas
Modifies EvaluateSwitch default clause handling: changes unconditional loop skip to conditional—only skip early when no prior match exists, allowing default to participate in fallthrough after a matched case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Through twisting cases our default now falls,
No longer trapped behind early Continue calls,
With matched sequences dancing through paths,
The rabbit's fixed logic smooths switch's baths! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the main fix: enabling the default clause to participate in fall-through behavior in switch statements.
Linked Issues check ✅ Passed The PR implementation in EvaluateSwitch correctly modifies the first-pass loop to skip default only when no prior match exists, allowing fall-through when Matched = True, and test cases validate this behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the switch default clause fall-through issue: the core logic fix in EvaluateSwitch and comprehensive test coverage for the behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/switch-default-fallthrough

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 8, 2026

Benchmark Results

254 benchmarks · 🔴 18 regressed · 236 unchanged · avg -2.3%

arraybuffer.js — 14 unchanged · avg +0.7%
Benchmark Base (ops/sec) PR (ops/sec) Change
create ArrayBuffer(0) 367,210 368,377 +0.3%
create ArrayBuffer(64) 361,285 363,239 +0.5%
create ArrayBuffer(1024) 284,371 285,826 +0.5%
create ArrayBuffer(8192) 132,101 132,070 -0.0%
slice full buffer (64 bytes) 444,702 447,432 +0.6%
slice half buffer (512 of 1024 bytes) 384,321 384,102 -0.1%
slice with negative indices 374,591 374,917 +0.1%
slice empty range 432,920 431,599 -0.3%
byteLength access 1,097,207 1,096,057 -0.1%
Symbol.toStringTag access 787,342 786,178 -0.1%
ArrayBuffer.isView 604,854 606,448 +0.3%
clone ArrayBuffer(64) 340,447 346,905 +1.9%
clone ArrayBuffer(1024) 265,593 274,757 +3.5%
clone ArrayBuffer inside object 236,935 243,200 +2.6%
arrays.js — 19 unchanged · avg +0.3%
Benchmark Base (ops/sec) PR (ops/sec) Change
Array.from length 100 12,872 12,777 -0.7%
Array.from 10 elements 206,606 204,386 -1.1%
Array.of 10 elements 280,066 276,975 -1.1%
spread into new array 276,482 278,806 +0.8%
map over 50 elements 23,675 23,858 +0.8%
filter over 50 elements 19,506 19,690 +0.9%
reduce sum 50 elements 22,394 22,485 +0.4%
forEach over 50 elements 17,169 17,396 +1.3%
find in 50 elements 29,536 29,748 +0.7%
sort 20 elements 9,764 10,148 +3.9%
flat nested array 100,947 100,354 -0.6%
flatMap 61,113 62,316 +2.0%
map inside map (5x5) 17,829 18,135 +1.7%
filter inside map (5x10) 13,038 13,026 -0.1%
reduce inside map (5x10) 15,662 15,580 -0.5%
forEach inside forEach (5x10) 12,652 12,573 -0.6%
find inside some (10x10) 11,763 11,567 -1.7%
map+filter chain nested (5x20) 4,547 4,469 -1.7%
reduce flatten (10x5) 33,342 33,518 +0.5%
async-await.js — 6 unchanged · avg -0.4%
Benchmark Base (ops/sec) PR (ops/sec) Change
single await 292,495 290,406 -0.7%
multiple awaits 127,315 127,421 +0.1%
await non-Promise value 606,550 606,561 +0.0%
await with try/catch 273,771 271,834 -0.7%
await Promise.all 45,204 45,346 +0.3%
nested async function call 146,128 144,335 -1.2%
classes.js — 31 unchanged · avg -1.4%
Benchmark Base (ops/sec) PR (ops/sec) Change
simple class new 97,298 98,035 +0.8%
class with defaults 74,450 74,426 -0.0%
50 instances via Array.from 4,206 4,222 +0.4%
instance method call 46,297 46,271 -0.1%
static method call 82,341 82,033 -0.4%
single-level inheritance 38,512 38,391 -0.3%
two-level inheritance 33,216 33,062 -0.5%
private field access 46,546 46,445 -0.2%
private methods 53,840 54,392 +1.0%
getter/setter access 55,526 54,941 -1.1%
class decorator (identity) 73,807 72,931 -1.2%
class decorator (wrapping) 41,044 40,946 -0.2%
identity method decorator 52,622 51,505 -2.1%
wrapping method decorator 43,037 41,909 -2.6%
stacked method decorators (x3) 30,420 30,331 -0.3%
identity field decorator 59,793 57,762 -3.4%
field initializer decorator 50,014 49,424 -1.2%
getter decorator (identity) 49,634 48,531 -2.2%
setter decorator (identity) 40,954 39,986 -2.4%
static method decorator 56,105 54,429 -3.0%
static field decorator 65,678 63,005 -4.1%
private method decorator 41,795 41,828 +0.1%
private field decorator 44,949 43,979 -2.2%
plain auto-accessor (no decorator) 78,115 75,065 -3.9%
auto-accessor with decorator 46,797 45,646 -2.5%
decorator writing metadata 35,743 35,506 -0.7%
static getter read 87,203 84,543 -3.0%
static getter/setter pair 63,846 62,711 -1.8%
inherited static getter 49,908 48,791 -2.2%
inherited static setter 53,306 52,025 -2.4%
inherited static getter with this binding 42,491 41,580 -2.1%
closures.js — 11 unchanged · avg -2.4%
Benchmark Base (ops/sec) PR (ops/sec) Change
closure over single variable 80,089 78,690 -1.7%
closure over multiple variables 95,200 91,963 -3.4%
nested closures 94,499 94,284 -0.2%
function as argument 74,165 73,211 -1.3%
function returning function 89,691 88,853 -0.9%
compose two functions 55,187 53,855 -2.4%
fn.call 122,922 118,286 -3.8%
fn.apply 90,642 88,645 -2.2%
fn.bind 112,947 107,450 -4.9%
recursive sum to 50 8,522 8,392 -1.5%
recursive tree traversal 13,812 13,304 -3.7%
collections.js — 12 unchanged · avg -1.3%
Benchmark Base (ops/sec) PR (ops/sec) Change
add 50 elements 6,607 6,591 -0.2%
has lookup (50 elements) 87,458 85,685 -2.0%
delete elements 46,952 45,545 -3.0%
forEach iteration 12,740 12,651 -0.7%
spread to array 27,749 27,234 -1.9%
deduplicate array 38,005 36,855 -3.0%
set 50 entries 4,807 4,737 -1.5%
get lookup (50 entries) 86,879 84,459 -2.8%
has check 124,641 124,571 -0.1%
delete entries 44,592 44,449 -0.3%
forEach iteration 12,610 12,885 +2.2%
keys/values/entries 7,491 7,349 -1.9%
destructuring.js — 22 unchanged · avg -1.9%
Benchmark Base (ops/sec) PR (ops/sec) Change
simple array destructuring 329,679 325,221 -1.4%
with rest element 226,452 228,035 +0.7%
with defaults 333,397 328,830 -1.4%
skip elements 342,984 338,243 -1.4%
nested array destructuring 138,817 135,997 -2.0%
swap variables 346,137 338,652 -2.2%
simple object destructuring 245,361 247,510 +0.9%
with defaults 306,434 295,327 -3.6%
with renaming 263,249 259,426 -1.5%
nested object destructuring 124,458 117,793 -5.4%
rest properties 163,169 157,708 -3.3%
object parameter 75,975 74,305 -2.2%
array parameter 99,342 97,178 -2.2%
mixed destructuring in map 30,484 29,752 -2.4%
forEach with array destructuring 52,515 49,900 -5.0%
map with array destructuring 55,096 52,213 -5.2%
filter with array destructuring 57,103 57,753 +1.1%
reduce with array destructuring 59,710 61,797 +3.5%
map with object destructuring 67,054 68,051 +1.5%
map with nested destructuring 60,077 55,952 -6.9%
map with rest in destructuring 32,005 31,717 -0.9%
map with defaults in destructuring 50,534 49,736 -1.6%
fibonacci.js — 8 unchanged · avg -0.8%
Benchmark Base (ops/sec) PR (ops/sec) Change
recursive fib(15) 244 235 -3.5%
recursive fib(20) 21 21 +0.5%
recursive fib(15) typed 241 236 -2.4%
recursive fib(20) typed 21 21 +0.8%
iterative fib(20) via reduce 9,422 9,265 -1.7%
iterator fib(20) 6,643 6,725 +1.2%
iterator fib(20) via Iterator.from + take 6,886 6,868 -0.3%
iterator fib(20) last value via reduce 6,076 5,991 -1.4%
for-of.js — 7 unchanged · avg -1.8%
Benchmark Base (ops/sec) PR (ops/sec) Change
for...of with 10-element array 37,635 36,714 -2.4%
for...of with 100-element array 4,211 4,296 +2.0%
for...of with string (10 chars) 28,092 27,829 -0.9%
for...of with Set (10 elements) 37,357 36,218 -3.0%
for...of with Map entries (10 entries) 25,176 24,258 -3.6%
for...of with destructuring 32,767 32,123 -2.0%
for-await-of with sync array 35,096 34,243 -2.4%
iterators.js — 🔴 4 regressed, 16 unchanged · avg -4.5%
Benchmark Base (ops/sec) PR (ops/sec) Change
Iterator.from({next}).toArray() — 20 elements 8,607 8,213 -4.6%
Iterator.from({next}).toArray() — 50 elements 3,858 3,525 🔴 -8.6%
spread pre-wrapped iterator — 20 elements 8,944 8,408 -6.0%
Iterator.from({next}).forEach — 50 elements 2,790 2,745 -1.6%
Iterator.from({next}).reduce — 50 elements 2,945 2,826 -4.0%
wrap array iterator 56,071 52,168 -7.0%
wrap plain {next()} object 6,077 5,590 🔴 -8.0%
map + toArray (50 elements) 2,440 2,385 -2.3%
filter + toArray (50 elements) 2,598 2,530 -2.6%
take(10) + toArray (50 element source) 13,883 13,024 -6.2%
drop(40) + toArray (50 element source) 3,482 3,333 -4.3%
chained map + filter + take (100 element source) 4,523 4,351 -3.8%
some + every (50 elements) 1,605 1,605 -0.0%
find (50 elements) 3,470 3,458 -0.4%
array.values().map().filter().toArray() 3,530 3,415 -3.3%
array.values().take(5).toArray() 59,504 55,212 🔴 -7.2%
array.values().drop(45).toArray() 16,761 15,266 🔴 -8.9%
map.entries() chained helpers 5,747 5,517 -4.0%
set.values() chained helpers 8,376 8,034 -4.1%
string iterator map + toArray 7,111 6,873 -3.3%
json.js — 🔴 4 regressed, 16 unchanged · avg -4.8%
Benchmark Base (ops/sec) PR (ops/sec) Change
parse simple object 152,189 149,590 -1.7%
parse nested object 99,438 90,815 🔴 -8.7%
parse array of objects 50,633 48,189 -4.8%
parse large flat object 47,332 46,489 -1.8%
parse mixed types 67,966 64,643 -4.9%
stringify simple object 144,440 134,011 🔴 -7.2%
stringify nested object 73,786 71,865 -2.6%
stringify array of objects 38,257 37,579 -1.8%
stringify mixed types 65,168 61,869 -5.1%
reviver doubles numbers 34,380 33,467 -2.7%
reviver filters properties 32,436 30,311 -6.6%
reviver on nested object 40,951 39,475 -3.6%
reviver on array 22,797 21,921 -3.8%
replacer function doubles numbers 37,033 34,503 -6.8%
replacer function excludes properties 47,062 44,335 -5.8%
array replacer (allowlist) 97,557 90,692 🔴 -7.0%
stringify with 2-space indent 76,883 72,176 -6.1%
stringify with tab indent 73,966 73,199 -1.0%
parse then stringify 47,498 43,330 🔴 -8.8%
stringify then parse 28,372 26,834 -5.4%
jsx.jsx — 🔴 1 regressed, 20 unchanged · avg -3.3%
Benchmark Base (ops/sec) PR (ops/sec) Change
simple element 180,054 179,445 -0.3%
self-closing element 182,788 183,325 +0.3%
element with string attribute 148,475 145,027 -2.3%
element with multiple attributes 125,724 126,688 +0.8%
element with expression attribute 136,719 135,442 -0.9%
text child 173,871 168,817 -2.9%
expression child 170,406 162,101 -4.9%
mixed text and expression 160,595 150,484 -6.3%
nested elements (3 levels) 64,582 62,154 -3.8%
sibling children 47,324 46,521 -1.7%
component element 119,227 119,459 +0.2%
component with children 73,081 71,810 -1.7%
dotted component 99,237 93,464 -5.8%
empty fragment 177,483 167,229 -5.8%
fragment with children 48,245 45,471 -5.7%
spread attributes 92,396 87,571 -5.2%
spread with overrides 81,777 75,224 🔴 -8.0%
shorthand props 127,664 122,552 -4.0%
nav bar structure 22,750 22,122 -2.8%
card component tree 25,976 24,992 -3.8%
10 list items via Array.from 11,941 11,502 -3.7%
numbers.js — 🔴 1 regressed, 10 unchanged · avg -5.0%
Benchmark Base (ops/sec) PR (ops/sec) Change
integer arithmetic 367,615 348,677 -5.2%
floating point arithmetic 399,560 377,407 -5.5%
number coercion 152,234 145,130 -4.7%
toFixed 92,602 89,364 -3.5%
toString 132,813 128,017 -3.6%
valueOf 203,879 186,640 🔴 -8.5%
toPrecision 125,269 119,282 -4.8%
Number.isNaN 248,005 237,921 -4.1%
Number.isFinite 242,687 236,226 -2.7%
Number.isInteger 240,390 223,801 -6.9%
Number.parseInt and parseFloat 206,508 195,727 -5.2%
objects.js — 🔴 3 regressed, 4 unchanged · avg -5.9%
Benchmark Base (ops/sec) PR (ops/sec) Change
create simple object 393,406 363,832 🔴 -7.5%
create nested object 182,810 170,751 -6.6%
create 50 objects via Array.from 8,158 7,363 🔴 -9.7%
property read 396,485 370,975 -6.4%
Object.keys 265,966 243,606 🔴 -8.4%
Object.entries 94,820 96,004 +1.2%
spread operator 152,038 146,284 -3.8%
promises.js — 🔴 4 regressed, 8 unchanged · avg -6.1%
Benchmark Base (ops/sec) PR (ops/sec) Change
Promise.resolve(value) 438,975 412,598 -6.0%
new Promise(resolve => resolve(value)) 158,136 144,961 🔴 -8.3%
Promise.reject(reason) 434,012 394,347 🔴 -9.1%
resolve + then (1 handler) 137,439 133,948 -2.5%
resolve + then chain (3 deep) 55,886 54,429 -2.6%
resolve + then chain (10 deep) 18,819 17,508 -7.0%
reject + catch + then 81,600 71,979 🔴 -11.8%
resolve + finally + then 65,669 61,611 -6.2%
Promise.all (5 resolved) 25,790 25,029 -2.9%
Promise.race (5 resolved) 27,609 26,306 -4.7%
Promise.allSettled (5 mixed) 22,494 21,873 -2.8%
Promise.any (5 mixed) 26,350 23,790 🔴 -9.7%
strings.js — 🔴 1 regressed, 10 unchanged · avg -3.5%
Benchmark Base (ops/sec) PR (ops/sec) Change
string concatenation 322,396 305,873 -5.1%
template literal 404,264 345,163 🔴 -14.6%
string repeat 343,470 340,167 -1.0%
split and join 119,593 113,512 -5.1%
indexOf and includes 136,603 135,413 -0.9%
toUpperCase and toLowerCase 206,999 202,779 -2.0%
slice and substring 132,963 127,571 -4.1%
trim operations 153,794 145,160 -5.6%
replace and replaceAll 166,430 166,573 +0.1%
startsWith and endsWith 107,779 108,180 +0.4%
padStart and padEnd 162,308 161,605 -0.4%
typed-arrays.js — 22 unchanged · avg +0.1%
Benchmark Base (ops/sec) PR (ops/sec) Change
new Int32Array(0) 256,337 252,777 -1.4%
new Int32Array(100) 243,652 240,860 -1.1%
new Int32Array(1000) 148,084 149,629 +1.0%
new Float64Array(100) 215,016 219,704 +2.2%
Int32Array.from([...]) 157,503 159,073 +1.0%
Int32Array.of(1, 2, 3, 4, 5) 258,914 258,820 -0.0%
sequential write 100 elements 2,742 2,792 +1.8%
sequential read 100 elements 2,851 2,879 +1.0%
Float64Array write 100 elements 2,625 2,619 -0.2%
fill(42) 45,241 44,856 -0.8%
slice() 183,583 183,056 -0.3%
map(x => x * 2) 6,815 6,964 +2.2%
filter(x => x > 50) 7,256 7,197 -0.8%
reduce (sum) 6,891 6,910 +0.3%
sort() 161,632 163,979 +1.5%
indexOf() 365,015 374,391 +2.6%
reverse() 305,761 293,706 -3.9%
create view over existing buffer 318,159 318,134 -0.0%
subarray() 372,623 387,836 +4.1%
set() from array 440,828 438,349 -0.6%
for-of loop 4,088 3,875 -5.2%
spread into array 16,814 16,661 -0.9%

Measured on ubuntu-latest x64. Changes within ±7% are considered insignificant.

@frostney frostney merged commit b6a68dd into main Mar 8, 2026
4 checks passed
@frostney frostney deleted the fix/switch-default-fallthrough branch March 8, 2026 00:37
@frostney frostney added the bug Something isn't working label Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EvaluateSwitch: default clause does not participate in fallthrough after a prior case match

2 participants