From 2f58013202b78b26690d643b1d0eb8e0beb2db1a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 8 Dec 2023 08:33:22 -0800 Subject: [PATCH] Initial support for pattern matching in switches Fixes https://github.com/google/google-java-format/issues/937, https://github.com/google/google-java-format/issues/880 PiperOrigin-RevId: 589140113 --- .../java/java21/Java21InputAstVisitor.java | 43 ++++++++++++++++++ .../java/java14/Java14InputAstVisitor.java | 17 ++++++- .../java/java21/Java21InputAstVisitor.java | 44 +++++++++++++++++++ .../javaformat/java/FileBasedTests.java | 2 +- .../javaformat/java/testdata/I880.input | 19 ++++++++ .../javaformat/java/testdata/I880.output | 18 ++++++++ .../java/testdata/SwitchDouble.input | 7 +++ .../java/testdata/SwitchDouble.output | 7 +++ .../java/testdata/SwitchRecord.input | 21 +++++++++ .../java/testdata/SwitchRecord.output | 44 +++++++++++++++++++ .../java/testdata/SwitchUnderscore.input | 8 ++++ .../java/testdata/SwitchUnderscore.output | 8 ++++ 12 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.input create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.output create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.input create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.output create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.input create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.output create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.input create mode 100644 palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.output diff --git a/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java index a96ef99e..ad936cae 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java @@ -17,7 +17,12 @@ import com.google.googlejavaformat.OpsBuilder; import com.google.googlejavaformat.java.java17.Java17InputAstVisitor; import com.sun.source.tree.CaseTree; +import com.sun.source.tree.ConstantCaseLabelTree; +import com.sun.source.tree.DeconstructionPatternTree; +import com.sun.source.tree.DefaultCaseLabelTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.PatternCaseLabelTree; +import com.sun.source.tree.PatternTree; /** * Extends {@link Java17InputAstVisitor} with support for AST nodes that were added or modified in @@ -33,4 +38,42 @@ public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) { protected ExpressionTree getGuard(final CaseTree node) { return node.getGuard(); } + + @Override + public Void visitDefaultCaseLabel(DefaultCaseLabelTree node, Void unused) { + token("default"); + return null; + } + + @Override + public Void visitPatternCaseLabel(PatternCaseLabelTree node, Void unused) { + scan(node.getPattern(), null); + return null; + } + + @Override + public Void visitConstantCaseLabel(ConstantCaseLabelTree node, Void aVoid) { + scan(node.getConstantExpression(), null); + return null; + } + + @Override + public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void unused) { + scan(node.getDeconstructor(), null); + builder.open(plusFour); + token("("); + builder.breakOp(); + boolean first = true; + for (PatternTree pattern : node.getNestedPatterns()) { + if (!first) { + token(","); + builder.breakOp(" "); + } + first = false; + scan(pattern, null); + } + builder.close(); + token(")"); + return null; + } } diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java index 4e66d264..4a8c3d5a 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java @@ -114,12 +114,18 @@ public Void visitBindingPattern(BindingPatternTree node, Void unused) { } private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { + builder.open(plusFour); if (modifiers != null) { builder.addAll(visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty())); } scan(type, null); builder.breakOp(" "); - visit(name); + if (name.isEmpty()) { + token("_"); + } else { + visit(name); + } + builder.close(); } @Override @@ -259,6 +265,11 @@ public Void visitCase(CaseTree node, Void unused) { labels = node.getExpressions(); isDefault = labels.isEmpty(); } + builder.open( + node.getCaseKind().equals(CaseTree.CaseKind.RULE) + && !node.getBody().getKind().equals(Tree.Kind.BLOCK) + ? plusFour + : ZERO); if (isDefault) { token("default", plusTwo); } else { @@ -301,8 +312,8 @@ public Void visitCase(CaseTree node, Void unused) { builder.space(); token("-"); token(">"); - builder.space(); if (node.getBody().getKind() == BLOCK) { + builder.space(); // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. visitBlock( (BlockTree) node.getBody(), @@ -310,6 +321,7 @@ public Void visitCase(CaseTree node, Void unused) { AllowLeadingBlankLine.NO, AllowTrailingBlankLine.NO); } else { + builder.breakOp(" "); scan(node.getBody(), null); } builder.guessToken(";"); @@ -318,6 +330,7 @@ public Void visitCase(CaseTree node, Void unused) { default: throw new IllegalArgumentException(node.getCaseKind().name()); } + builder.close(); return null; } diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java index b727d84d..1edd46ca 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java @@ -19,7 +19,13 @@ import com.palantir.javaformat.OpsBuilder; import com.palantir.javaformat.java.java14.Java14InputAstVisitor; import com.sun.source.tree.CaseTree; +import com.sun.source.tree.ConstantCaseLabelTree; +import com.sun.source.tree.DeconstructionPatternTree; +import com.sun.source.tree.DefaultCaseLabelTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.PatternCaseLabelTree; +import com.sun.source.tree.PatternTree; + /** * Extends {@link Java14InputAstVisitor} with support for AST nodes that were added or modified in * Java 21. @@ -33,4 +39,42 @@ public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) { protected ExpressionTree getGuard(final CaseTree node) { return node.getGuard(); } + + @Override + public Void visitDefaultCaseLabel(DefaultCaseLabelTree node, Void unused) { + token("default"); + return null; + } + + @Override + public Void visitPatternCaseLabel(PatternCaseLabelTree node, Void unused) { + scan(node.getPattern(), null); + return null; + } + + @Override + public Void visitConstantCaseLabel(ConstantCaseLabelTree node, Void aVoid) { + scan(node.getConstantExpression(), null); + return null; + } + + @Override + public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void unused) { + scan(node.getDeconstructor(), null); + builder.open(plusFour); + token("("); + builder.breakOp(); + boolean first = true; + for (PatternTree pattern : node.getNestedPatterns()) { + if (!first) { + token(","); + builder.breakOp(" "); + } + first = false; + scan(pattern, null); + } + builder.close(); + token(")"); + return null; + } } \ No newline at end of file diff --git a/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java b/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java index 4f4000e7..dcc72c75 100644 --- a/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java +++ b/palantir-java-format/src/test/java/com/palantir/javaformat/java/FileBasedTests.java @@ -50,7 +50,7 @@ public final class FileBasedTests { .putAll(15, "I603") .putAll(16, "I588") .putAll(17, "I683", "I684", "I696") - .putAll(21, "SwitchGuardClause") + .putAll(21, "SwitchGuardClause", "SwitchRecord", "SwitchDouble", "SwitchUnderscore", "I880") .build(); private final Class testClass; diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.input b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.input new file mode 100644 index 00000000..dfc8a4cf --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.input @@ -0,0 +1,19 @@ +class I880 { + public String f(int i) { + return switch (i) { + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -> "looooooooooooooooooooooooooooooooooooooooong expression"; + default -> "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong expression"; + }; + } + + public boolean test(int i) { + return switch (i) { + case 0 -> // zero + false; + case 1 -> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".length() + == 0; + default -> // otherwise + true; + }; + } +} \ No newline at end of file diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.output b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.output new file mode 100644 index 00000000..ebea3a63 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/I880.output @@ -0,0 +1,18 @@ +class I880 { + public String f(int i) { + return switch (i) { + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -> "looooooooooooooooooooooooooooooooooooooooong expression"; + default -> "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong expression"; + }; + } + + public boolean test(int i) { + return switch (i) { + case 0 -> // zero + false; + case 1 -> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".length() == 0; + default -> // otherwise + true; + }; + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.input b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.input new file mode 100644 index 00000000..54e22a76 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.input @@ -0,0 +1,7 @@ +class SwitchDouble { + void x(Object o) { + switch (o) { + case null, default: + } + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.output b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.output new file mode 100644 index 00000000..ed311900 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchDouble.output @@ -0,0 +1,7 @@ +class SwitchDouble { + void x(Object o) { + switch (o) { + case null, default: + } + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.input b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.input new file mode 100644 index 00000000..2f4fb35d --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.input @@ -0,0 +1,21 @@ +record SwitchRecord(int i) { + int x(Object o) { + return switch (o) { + case SwitchRecord(int i) -> i; + default -> 0; + }; + } + int f(Object o) { + return switch (o) { + case SwitchRecord(int one, int two, int three, int four, int five, int six, int seven, int eight, int nine) -> nine; + default -> 0; + }; + } + int g(Object o) { + return switch (o) { + case SwitchRecord(int one, int two, int three, int four, int five, int six, int seven, int eight, int nine) -> + System.err.println("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."); + default -> 0; + }; + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.output b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.output new file mode 100644 index 00000000..38659b62 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchRecord.output @@ -0,0 +1,44 @@ +record SwitchRecord(int i) { + int x(Object o) { + return switch (o) { + case SwitchRecord(int i) -> i; + default -> 0; + }; + } + + int f(Object o) { + return switch (o) { + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int nine) -> nine; + default -> 0; + }; + } + + int g(Object o) { + return switch (o) { + case SwitchRecord( + int one, + int two, + int three, + int four, + int five, + int six, + int seven, + int eight, + int nine) -> System.err.println( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" + + " incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis" + + " nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo" + + " consequat."); + default -> 0; + }; + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.input b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.input new file mode 100644 index 00000000..8d611d24 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.input @@ -0,0 +1,8 @@ +public class SwitchUnderscore { + void x(Object o) { + switch (o) { + case String _: + default: + } + } +} diff --git a/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.output b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.output new file mode 100644 index 00000000..6ea58e55 --- /dev/null +++ b/palantir-java-format/src/test/resources/com/palantir/javaformat/java/testdata/SwitchUnderscore.output @@ -0,0 +1,8 @@ +public class SwitchUnderscore { + void x(Object o) { + switch (o) { + case String _: + default: + } + } +}