Support Java 21 record patterns (#67) - #167
Merged
Merged
Conversation
Converts Java 21 record patterns (JEP 440) into C# patterns: - `instanceof` with a type or record pattern becomes a C# `is` pattern, so `o instanceof Point(int x, int y)` converts to a positional pattern. - Pattern labels in switch expressions and switch statements convert to C# patterns, including nested deconstruction and `var` components. - Java 21 `when` guards convert to C# `when` clauses. The parser language level is raised from Java 17 to Java 21, without which record patterns fail to parse at all. Switch statement break handling is also corrected. Java's arrow form never falls through, but previously only the default case received an implicit `break`, so an arrow-form pattern case would emit C# that fails to compile with CS0163. Breaks are now added for every arrow-form entry and suppressed when the section already ends in a jump statement, which additionally removes some unreachable breaks the old code emitted after a `return`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
paulirwin
marked this pull request as ready for review
August 16, 2026 21:09
The record pattern resource was in GeneralSuccessfulConversionTest, which only asserts the result is non-null. That catches conversion throwing but says nothing about whether the emitted C# is correct. Move it to FullIntegrationTests, which compiles the generated C# with Roslyn, executes it, and asserts on the runtime output declared in the resource's `/// - output:` header. The resource is rewritten as a runnable program covering instanceof, switch expressions, both arrow and colon switch statements, guards, and nested deconstruction. The expected output is the verbatim output of the Java source run under a JDK, so the test pins the converted C# to Java's actual behavior rather than to a hand-written guess. 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 #67.
Converts Java 21 record patterns (JEP 440) into the equivalent C# patterns.
What's supported
o instanceof Point(int x, int y)o is Point (int x, int y)o instanceof String so is string scase Point(int x, int y) ->case Point (int x, int y):case Point(int x, int y) when x > y ->case Point (int x, int y) when x > y:Line(Point(var ax, var ay), Point end)Line (Point (var ax, var ay), Point end)Nested deconstruction,
varcomponents, andwhenguards all work, ininstanceoftests as well as both switch expressions and switch statements.Java's type patterns map onto C# declaration patterns and its record deconstruction patterns map onto C# positional patterns, so the conversion is largely structural. New
PatternExpressionVisitorholds that logic so theinstanceofand both switch paths share it.Unblocking the parse
The issue was labelled
blockedpending javaparser support. That's resolved — javaparser 3.27.0 (already referenced here) hasRecordPatternExprandTypePatternExpr.However, the parser language level was pinned to
JAVA_17, so record patterns failed at parse time with "Record patterns are not supported... the language level must be configured" before conversion was ever reached. This raises it toJAVA_21.Drive-by fix: switch statement
breakhandlingAdding pattern labels surfaced a latent bug. Java's arrow form never falls through, but only the default case was getting an implicit
break. That was invisible while every arrow case was a constant, but an arrow-form pattern case emitted C# that fails to compile:Breaks are now added for every arrow-form entry, and suppressed when the section already ends in a jump statement. That last part also removes unreachable breaks the old code emitted after a
return— the previous logic scanned all statements for a break rather than just the last one.Colon-form entries are untouched and keep Java's explicit fallthrough semantics.
Testing
Java21RecordPatterns.javais wired intoFullIntegrationTests, which compiles the generated C# with Roslyn, executes it, and asserts on the runtime output declared in the resource's/// - output:header. So the test verifies the patterns actually match correctly at runtime, not just that plausible-looking text was emitted.The expected output is the verbatim output of running the Java source under a JDK, which pins the converted C# to Java's real behaviour rather than to a hand-written guess. (Writing it by hand got one value wrong; running the Java caught it.)
That's backed by 10 unit tests in
ConvertRecordPatternTestscovering the emitted syntax for each form, including the break/fallthrough behaviour.Full suite: 329 passed, 0 failed (318 before this change).
Note on generics
Box<?>(String value)converts toBox<TWildcardTodo> (string value). That's this repo's existing placeholder for Java wildcards (TypeNameParser), not specific to record patterns, so it's left as-is and out of the test resource.🤖 Generated with Claude Code