Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support JEP-441: Pattern Matching for switch for Java 21+ #4661

Merged
merged 41 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
10e8435
Add test for parsing JEP440
nielsdebruin Nov 11, 2024
87bfce9
Add test for parsing JEP441
nielsdebruin Nov 11, 2024
53166de
Merge branch 'jep-440-441' into 4660-support-jep-440-and-441
nielsdebruin Nov 11, 2024
212fd51
Update Java21ParserTest.java
nielsdebruin Nov 13, 2024
3a2b164
Merge branch 'main' into 4660-support-jep-440-and-441
nielsdebruin Nov 15, 2024
9fbe1e9
Merge branch 'main' into 4660-support-jep-440-and-441
nielsdebruin Nov 18, 2024
5af2efd
Merge branch 'main' into 4660-support-jep-440-and-441
nielsdebruin Nov 30, 2024
c47520e
Merge branch 'main' into 4660-support-jep-440-and-441
nielsdebruin Dec 16, 2024
b4ad50f
Merge branch 'main' into 4660-support-jep-440-and-441
nielsdebruin Jan 7, 2025
91f76e7
Start on implementation
Laurens-W Jan 7, 2025
96633f2
Pattern matching on switch works
Laurens-W Jan 8, 2025
d50ad0a
Format and clean up
Laurens-W Jan 8, 2025
86d50f5
Remove public from tests
Laurens-W Jan 8, 2025
560a45e
Fix license
Laurens-W Jan 8, 2025
c7b8faa
Make labels nullable
Laurens-W Jan 8, 2025
9a712e6
Disable record pattern matching tests as that has yet to be implemented
Laurens-W Jan 9, 2025
18f7342
Remove tests in wrong place
Laurens-W Jan 9, 2025
2b8635b
Remove unused imports
Laurens-W Jan 9, 2025
8797243
Merge branch 'main' into 4660-support-jep-440-and-441
Laurens-W Jan 9, 2025
5da6b04
Also map guard
Laurens-W Jan 9, 2025
0914a70
Remove case label
Laurens-W Jan 9, 2025
ebfa04f
Merge branch 'main' into 4660-support-jep-440-and-441
Laurens-W Jan 10, 2025
636da25
Fixed
Laurens-W Jan 13, 2025
db4701d
Merge branch 'main' into 4660-support-jep-440-and-441
Laurens-W Jan 13, 2025
9c3115e
Remove unused
Laurens-W Jan 13, 2025
f99762f
Simplify test
Laurens-W Jan 13, 2025
fe2b1f7
Remove unpadded guard from Padding subclass
Laurens-W Jan 14, 2025
d328eab
Reintroduce case label. In order to properly map both expressions and…
Laurens-W Jan 14, 2025
5e69813
Add Record pattern matching print idempotency Java21 (#4900)
Laurens-W Jan 14, 2025
98ed372
Merge branch 'main' into 4660-support-jep-440-and-441
timtebeek Jan 14, 2025
a2add78
Keep a single list of caseLabels and add logic to transform Expressio…
Laurens-W Jan 16, 2025
9bce27f
Merge branch 'main' into 4660-support-jep-440-and-441
Laurens-W Jan 16, 2025
d833153
Fix compilation and remove unnecessary change
Laurens-W Jan 16, 2025
74b9e1f
Oh
Laurens-W Jan 16, 2025
06fa88b
Remove noise
Laurens-W Jan 16, 2025
a7af194
Combine branches
Laurens-W Jan 16, 2025
966c3bb
Apply review feedback
Laurens-W Jan 17, 2025
95c9276
Apply review feedback
Laurens-W Jan 17, 2025
4805f8d
Formatting
Laurens-W Jan 17, 2025
c6bff7b
Take when before case guard into account
Laurens-W Jan 17, 2025
472082f
Apply safety check
Laurens-W Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ public List<J.Annotation> visitAndGetAnnotations(AnnotatedNode node) {
for (AnnotationNode annotationNode : node.getAnnotations()) {
// The groovy compiler can add or remove annotations for AST transformations.
// Because @groovy.transform.Immutable is discarded in favour of other transform annotations, the removed annotation must be parsed by hand.
if (sourceStartsWith("@" + Immutable.class.getSimpleName()) || sourceStartsWith("@" + Immutable.class.getCanonicalName()) ) {
if (sourceStartsWith("@" + Immutable.class.getSimpleName()) || sourceStartsWith("@" + Immutable.class.getCanonicalName())) {
visitAnnotation(new AnnotationNode(new ClassNode(Immutable.class)));
paramAnnotations.add(pollQueue());
}
Expand Down Expand Up @@ -1135,10 +1135,12 @@ public void visitCaseStatement(CaseStatement statement) {
J.Case.Type.Statement,
null,
JContainer.build(singletonList(JRightPadded.build(visit(statement.getExpression())))),
null,
null,
statement.getCode() instanceof EmptyStatement ?
JContainer.build(sourceBefore(":"), convertStatements(emptyList()), Markers.EMPTY) :
JContainer.build(sourceBefore(":"), convertStatements(((BlockStatement) statement.getCode()).getStatements()), Markers.EMPTY)
, null)
JContainer.build(sourceBefore(":"), convertStatements(((BlockStatement) statement.getCode()).getStatements()), Markers.EMPTY),
null)
);
}

Expand All @@ -1149,6 +1151,8 @@ private J.Case visitDefaultCaseStatement(BlockStatement statement) {
J.Case.Type.Statement,
null,
JContainer.build(singletonList(JRightPadded.build(new J.Identifier(randomId(), EMPTY, Markers.EMPTY, emptyList(), skip("default"), null, null)))),
null,
null,
JContainer.build(sourceBefore(":"), convertStatements(statement.getStatements()), Markers.EMPTY),
null
);
Expand Down Expand Up @@ -1609,7 +1613,7 @@ public void visitGStringExpression(GStringExpression gstring) {
}
}

queue.add(new G.GString(randomId(), fmt, Markers.EMPTY, delimiter, strings,typeMapping.type(gstring.getType())));
queue.add(new G.GString(randomId(), fmt, Markers.EMPTY, delimiter, strings, typeMapping.type(gstring.getType())));
skip(delimiter); // Closing delim for GString
}

Expand Down Expand Up @@ -2778,6 +2782,7 @@ private static ClassNode staticType(Parameter parameter) {
}

private static final Map<String, J.Modifier.Type> modifierNameToType;

static {
modifierNameToType = new LinkedHashMap<>();
modifierNameToType.put("def", J.Modifier.Type.LanguageExtension);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ public J visitCase(CaseTree node, Space fmt) {
),
Markers.EMPTY
),
null,
null,
JContainer.build(sourceBefore(":"), convertStatements(node.getStatements()), Markers.EMPTY),
null
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ public J visitCase(CaseTree node, Space fmt) {
convertAll(node.getExpressions(), commaDelim, t -> EMPTY),
Markers.EMPTY
),
null,
null,
knutwannheden marked this conversation as resolved.
Show resolved Hide resolved
JContainer.build(
sourceBefore(type == J.Case.Type.Rule ? "->" : ":"),
convertStatements(node.getStatements()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,15 @@ public J visitCase(CaseTree node, Space fmt) {
Markers.EMPTY,
type,
null,
null,
JContainer.build(
node.getExpressions().isEmpty() ? EMPTY : sourceBefore("case"),
node.getExpressions().isEmpty() ?
node.getLabels().isEmpty() ? EMPTY : sourceBefore("case"),
node.getLabels().isEmpty() || node.getLabels().getFirst() instanceof DefaultCaseLabelTree ?
List.of(JRightPadded.build(new J.Identifier(randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), skip("default"), null, null))) :
convertAll(node.getExpressions(), commaDelim, t -> EMPTY),
convertAll(node.getLabels(), commaDelim, ignored -> node.getGuard() != null ? sourceBefore("when", '-') : EMPTY),
Markers.EMPTY
),
convert(node.getGuard()),
JContainer.build(
sourceBefore(type == J.Case.Type.Rule ? "->" : ":"),
convertStatements(node.getStatements()),
Expand Down Expand Up @@ -779,12 +781,26 @@ public J visitInstanceOf(InstanceOfTree node, Space fmt) {
return new J.InstanceOf(randomId(), fmt, Markers.EMPTY,
convert(node.getExpression(), t -> sourceBefore("instanceof")),
convert(node.getType()),
node.getPattern() instanceof JCBindingPattern b ?
new J.Identifier(randomId(), sourceBefore(b.getVariable().getName().toString()), Markers.EMPTY, emptyList(), b.getVariable().getName().toString(),
type, typeMapping.variableType(b.var.sym)) : null,
getNodePattern(node.getPattern(), type),
type);
}

private @Nullable J getNodePattern(@Nullable PatternTree pattern, JavaType type) {
if (pattern instanceof JCBindingPattern b) {
return new J.Identifier(randomId(), sourceBefore(b.getVariable().getName().toString()), Markers.EMPTY, emptyList(), b.getVariable().getName().toString(),
type, typeMapping.variableType(b.var.sym));
} else {
if (pattern == null) {
return null;
}
int saveCursor = cursor;
int endCursor = max(endPos(pattern), cursor);
cursor = endCursor;
return new J.Unknown(randomId(), whitespace(), Markers.EMPTY, new J.Unknown.Source(randomId(), whitespace(), Markers.EMPTY, source.substring(saveCursor, endCursor)));

}
}

@Override
public J visitIntersectionType(IntersectionTypeTree node, Space fmt) {
JContainer<TypeTree> bounds = node.getBounds().isEmpty() ? null :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ public J visitCase(CaseTree node, Space fmt) {
),
Markers.EMPTY
),
null,
null,
JContainer.build(sourceBefore(":"), convertStatements(node.getStatements()), Markers.EMPTY),
null
);
Expand Down Expand Up @@ -1787,17 +1789,17 @@ private <J2 extends J> List<JRightPadded<J2>> convertAll(List<? extends Tree> tr

private Space statementDelim(@Nullable Tree t) {
if (t instanceof JCAssert ||
t instanceof JCAssign ||
t instanceof JCAssignOp ||
t instanceof JCBreak ||
t instanceof JCContinue ||
t instanceof JCDoWhileLoop ||
t instanceof JCImport ||
t instanceof JCMethodInvocation ||
t instanceof JCNewClass ||
t instanceof JCReturn ||
t instanceof JCThrow ||
t instanceof JCUnary) {
t instanceof JCAssign ||
t instanceof JCAssignOp ||
t instanceof JCBreak ||
t instanceof JCContinue ||
t instanceof JCDoWhileLoop ||
t instanceof JCImport ||
t instanceof JCMethodInvocation ||
t instanceof JCNewClass ||
t instanceof JCReturn ||
t instanceof JCThrow ||
t instanceof JCUnary) {
return sourceBefore(";");
}

Expand All @@ -1808,7 +1810,7 @@ private Space statementDelim(@Nullable Tree t) {
if (t instanceof JCExpressionStatement) {
ExpressionTree expTree = ((ExpressionStatementTree) t).getExpression();
if (expTree instanceof ErroneousTree) {
return Space.build(source.substring(((JCTree) expTree).getEndPosition(endPosTable),((JCTree) t).getEndPosition(endPosTable)), Collections.emptyList());
return Space.build(source.substring(((JCTree) expTree).getEndPosition(endPosTable), ((JCTree) t).getEndPosition(endPosTable)), Collections.emptyList());
} else {
return sourceBefore(";");
}
Expand Down Expand Up @@ -1972,7 +1974,7 @@ private int positionOfNext(String untilDelim, @Nullable Character stop) {
char c2 = source.charAt(delimIndex + 1);
switch (c1) {
case '/':
switch(c2) {
switch (c2) {
case '/':
inSingleLineComment = true;
delimIndex++;
Expand All @@ -1984,7 +1986,7 @@ private int positionOfNext(String untilDelim, @Nullable Character stop) {
}
break;
case '*':
if(c2 == '/') {
if (c2 == '/') {
inMultiLineComment = false;
delimIndex++;
continue;
Expand Down Expand Up @@ -2051,7 +2053,7 @@ private List<String> listFlags(long flags) {
try {
// FIXME instanceof probably not right here...
return field.get(null) instanceof Long &&
field.getName().matches("[A-Z_]+");
field.getName().matches("[A-Z_]+");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.tree;

import org.junit.jupiter.api.Test;
import org.openrewrite.java.MinimumJava21;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;
import org.openrewrite.test.TypeValidation;

import static org.openrewrite.java.Assertions.java;

@MinimumJava21
class RecordPatternMatchingTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.typeValidationOptions(TypeValidation.all().unknown(false));
}

@Test
void shouldParseJava21PatternMatchForRecords() {
rewriteRun(
java(
//language=java
"""
record Point(int x, int y) {}
class Test {
void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
}
"""
));
}

@Test
void shouldParseJava21NestedPatternMatchForRecords() {
rewriteRun(
java(
//language=java
"""
record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
class Test {
void printColorOfUpperLeftPoint(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
ColoredPoint lr)) {
System.out.println(c);
}
}
}
"""
));
}

}
Loading
Loading