Support Java 21 switch pattern matching (#68) - #168
Merged
Conversation
Record pattern support (#67) already covered most of JEP 441, since pattern labels, `when` guards, and type patterns in both switch expressions and switch statements were needed to place record patterns in switch positions. This fixes the one construct it missed. javaparser models `case null, default` as a single null label carrying a separate isDefault flag, and both switch visitors keyed off the label list alone. The default half was silently dropped, so the arm matched only null. A non-null value matching no other arm then threw SwitchExpressionException instead of taking the arm, having returned a value under Java. The generated code compiled, so nothing surfaced this until runtime. Both visitors now consult isDefault(). C#'s discard pattern and default section both already match null, so the combined form collapses onto them. Adds Java21SwitchPatternMatching.java to the executing integration tests, covering the null label, both combined forms, guards, exhaustive switching over a sealed hierarchy, and type patterns over unrelated types. Its expected output is the verbatim output of the Java source run under a JDK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #68.
Record pattern support (#67) already covered most of JEP 441 — pattern labels,
whenguards, and type patterns in both switch expressions and switch statements were all prerequisites for putting record patterns in switch positions. This PR fixes the one construct that slipped through and adds end-to-end coverage for the JEP as a whole.The bug:
case null, defaultsilently dropped its defaultjavaparser models
case null, defaultas a single null label carrying a separateisDefault()flag:Both switch visitors keyed off the label list alone, so the
defaulthalf was invisible:The arm ended up matching only null, so a non-null value matching no other arm threw
SwitchExpressionExceptionwhere Java returned a value. The generated code still compiled — C# only warns (CS8509) about the now-inexhaustive switch — so this failed at runtime rather than at conversion or build time.Both visitors now consult
isDefault(). C#'s discard pattern anddefault:section already match null (verified both), so the combined form collapses cleanly onto them.What was already working
Confirmed by running each construct through the converter and comparing against a JDK:
whenguardscase nullon its owncase null, defaultTesting
Java21SwitchPatternMatching.javais wired intoFullIntegrationTests, which compiles the generated C#, executes it, and asserts on runtime output. It covers the null label, both combinedcase null, defaultforms (expression and statement), guards, exhaustive switching over a sealed hierarchy, and type patterns over unrelated types.The expected output is the verbatim output of running the Java source under a JDK, so the test pins the conversion to Java's real behaviour.
Plus 5 unit tests in
ConvertSwitchPatternTests. I verified the two coveringcase null, defaultfail against the unfixed visitors and pass with the fix, so they genuinely pin the bug rather than just passing.Full suite: 335 passed, 0 failed (329 before this change).
Two pre-existing bugs found while writing the resource
Both reproduce on
masterwithout any of these changes, so they're left alone and worked around in the test resource. Happy to file issues:record Circle(int r)keeps the lowercase propertyr, but a call toc.r()converts toc.R()— so the generated code doesn't compile. The resource uses deconstruction instead.longliteral suffix dropped.Object o = 9Lconverts toobject o = 9, which boxes asint. In a type-pattern switch that meanscase Long lnever matches and the value takes theIntegerarm instead. The resource avoidsLongpatterns.🤖 Generated with Claude Code