diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java index 5aa7a1233..e3b2b3235 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -155,10 +155,6 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept visitor = createVisitor( "com.google.googlejavaformat.java.java21.Java21InputAstVisitor", builder, options); - } else if (Runtime.version().feature() >= 17) { - visitor = - createVisitor( - "com.google.googlejavaformat.java.java17.Java17InputAstVisitor", builder, options); } else { visitor = new JavaInputAstVisitor(builder, options.indentationMultiplier()); } diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index 01f9a3e05..dcaa930e4 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -14,6 +14,7 @@ package com.google.googlejavaformat.java; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.googlejavaformat.Doc.FillMode.INDEPENDENT; @@ -84,8 +85,10 @@ import com.sun.source.tree.AssertTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; +import com.sun.source.tree.BindingPatternTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.BreakTree; +import com.sun.source.tree.CaseLabelTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.CatchTree; import com.sun.source.tree.ClassTree; @@ -125,6 +128,7 @@ import com.sun.source.tree.RequiresTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; +import com.sun.source.tree.SwitchExpressionTree; import com.sun.source.tree.SwitchTree; import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; @@ -138,11 +142,13 @@ import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.tree.WildcardTree; +import com.sun.source.tree.YieldTree; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeScanner; import java.util.ArrayDeque; import java.util.ArrayList; @@ -410,7 +416,17 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) { return null; } - protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) {} + protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) { + ModuleTree module = node.getModule(); + if (module != null) { + if (afterFirstToken) { + builder.blankLineWanted(YES); + } + markForPartialFormat(); + visitModule(module, null); + builder.forcedBreak(); + } + } /** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */ protected void dropEmptyDeclarations() { @@ -436,6 +452,9 @@ public Void visitClass(ClassTree tree, Void unused) { case ENUM: visitEnumDeclaration(tree); break; + case RECORD: + visitRecordDeclaration(tree); + break; default: throw new AssertionError(tree.getKind()); } @@ -928,6 +947,69 @@ public boolean visitEnumDeclaration(ClassTree node) { return false; } + public void visitRecordDeclaration(ClassTree node) { + sync(node); + typeDeclarationModifiers(node.getModifiers()); + Verify.verify(node.getExtendsClause() == null); + boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); + token("record"); + builder.space(); + visit(node.getSimpleName()); + if (!node.getTypeParameters().isEmpty()) { + token("<"); + } + builder.open(plusFour); + { + if (!node.getTypeParameters().isEmpty()) { + typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); + } + ImmutableList parameters = JavaInputAstVisitor.recordVariables(node); + token("("); + if (!parameters.isEmpty()) { + // Break before args. + builder.breakToFill(""); + } + // record headers can't declare receiver parameters + visitFormals(/* receiver= */ Optional.empty(), parameters); + token(")"); + if (hasSuperInterfaceTypes) { + builder.breakToFill(" "); + builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); + token("implements"); + builder.space(); + boolean afterFirstToken = false; + for (Tree superInterfaceType : node.getImplementsClause()) { + if (afterFirstToken) { + token(","); + builder.breakOp(" "); + } + scan(superInterfaceType, null); + afterFirstToken = true; + } + builder.close(); + } + } + builder.close(); + if (node.getMembers() == null) { + token(";"); + } else { + ImmutableList members = + node.getMembers().stream() + .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) + .collect(toImmutableList()); + addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); + } + dropEmptyDeclarations(); + } + + private static ImmutableList recordVariables(ClassTree node) { + return node.getMembers().stream() + .filter(JCTree.JCVariableDecl.class::isInstance) + .map(JCTree.JCVariableDecl.class::cast) + .filter(m -> (m.mods.flags & RECORD) == RECORD) + .collect(toImmutableList()); + } + @Override public Void visitMemberReference(MemberReferenceTree node, Void unused) { builder.open(plusFour); @@ -1199,7 +1281,11 @@ public Void visitInstanceOf(InstanceOfTree node, Void unused) { builder.open(ZERO); token("instanceof"); builder.breakOp(" "); - scan(node.getType(), null); + if (node.getPattern() != null) { + scan(node.getPattern(), null); + } else { + scan(node.getType(), null); + } builder.close(); builder.close(); return null; @@ -1874,18 +1960,69 @@ public Void visitCase(CaseTree node, Void unused) { sync(node); markForPartialFormat(); builder.forcedBreak(); - if (node.getExpression() == null) { + List labels = node.getLabels(); + boolean isDefault = + labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL"); + builder.open(node.getCaseKind().equals(CaseTree.CaseKind.RULE) ? plusFour : ZERO); + if (isDefault) { token("default", ZERO); - token(":"); } else { token("case", ZERO); + builder.open(ZERO); builder.space(); - scan(node.getExpression(), null); - token(":"); + boolean afterFirstToken = false; + for (Tree expression : labels) { + if (afterFirstToken) { + token(","); + builder.breakOp(" "); + } + scan(expression, null); + afterFirstToken = true; + } + builder.close(); + } + + final ExpressionTree guard = getGuard(node); + if (guard != null) { + builder.breakToFill(" "); + token("when"); + builder.space(); + scan(guard, null); + } + + switch (node.getCaseKind()) { + case STATEMENT: + token(":"); + builder.open(plusTwo); + visitStatements(node.getStatements()); + builder.close(); + builder.close(); + break; + case RULE: + builder.space(); + token("-"); + token(">"); + if (node.getBody().getKind() == BLOCK) { + builder.close(); + builder.space(); + // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. + visitBlock( + (BlockTree) node.getBody(), + CollapseEmptyOrNot.YES, + AllowLeadingBlankLine.NO, + AllowTrailingBlankLine.NO); + } else { + builder.breakOp(" "); + scan(node.getBody(), null); + builder.close(); + } + builder.guessToken(";"); + break; } - builder.open(plusTwo); - visitStatements(node.getStatements()); - builder.close(); + return null; + } + + protected ExpressionTree getGuard(final CaseTree node) { return null; } @@ -2022,7 +2159,7 @@ public Void visitTry(TryTree node, Void unused) { public void visitClassDeclaration(ClassTree node) { sync(node); typeDeclarationModifiers(node.getModifiers()); - List permitsTypes = getPermitsClause(node); + List permitsTypes = node.getPermitsClause(); boolean hasSuperclassType = node.getExtendsClause() != null; boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); boolean hasPermitsTypes = !permitsTypes.isEmpty(); @@ -3800,11 +3937,6 @@ protected void addBodyDeclarations( } } - /** Gets the permits clause for the given node. This is only available in Java 15 and later. */ - protected List getPermitsClause(ClassTree node) { - return ImmutableList.of(); - } - private void classDeclarationTypeList(String token, List types) { if (types.isEmpty()) { return; @@ -3966,4 +4098,40 @@ final BreakTag genSym() { public final String toString() { return MoreObjects.toStringHelper(this).add("builder", builder).toString(); } + + @Override + public Void visitBindingPattern(BindingPatternTree node, Void unused) { + sync(node); + VariableTree variableTree = node.getVariable(); + declareOne( + DeclarationKind.PARAMETER, + Direction.HORIZONTAL, + Optional.of(variableTree.getModifiers()), + variableTree.getType(), + variableTree.getName(), + /* op= */ "", + /* equals= */ "", + /* initializer= */ Optional.empty(), + /* trailing= */ Optional.empty(), + /* receiverExpression= */ Optional.empty(), + /* typeWithDims= */ Optional.empty()); + return null; + } + + @Override + public Void visitYield(YieldTree node, Void aVoid) { + sync(node); + token("yield"); + builder.space(); + scan(node.getValue(), null); + token(";"); + return null; + } + + @Override + public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { + sync(node); + visitSwitch(node.getExpression(), node.getCases()); + return null; + } } diff --git a/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java deleted file mode 100644 index 315523585..000000000 --- a/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2020 Google Inc. - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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 com.google.googlejavaformat.java.java17; - -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; - -import com.google.common.base.Verify; -import com.google.common.collect.ImmutableList; -import com.google.googlejavaformat.OpsBuilder; -import com.google.googlejavaformat.OpsBuilder.BlankLineWanted; -import com.google.googlejavaformat.java.JavaInputAstVisitor; -import com.sun.source.tree.BindingPatternTree; -import com.sun.source.tree.BlockTree; -import com.sun.source.tree.CaseLabelTree; -import com.sun.source.tree.CaseTree; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.InstanceOfTree; -import com.sun.source.tree.ModifiersTree; -import com.sun.source.tree.ModuleTree; -import com.sun.source.tree.SwitchExpressionTree; -import com.sun.source.tree.Tree; -import com.sun.source.tree.VariableTree; -import com.sun.source.tree.YieldTree; -import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.tree.TreeInfo; -import java.util.List; -import java.util.Optional; -import javax.lang.model.element.Name; - -/** - * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in - * Java 17. - */ -public class Java17InputAstVisitor extends JavaInputAstVisitor { - - public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) { - super(builder, indentMultiplier); - } - - @Override - protected void handleModule(boolean afterFirstToken, CompilationUnitTree node) { - ModuleTree module = node.getModule(); - if (module != null) { - if (afterFirstToken) { - builder.blankLineWanted(BlankLineWanted.YES); - } - markForPartialFormat(); - visitModule(module, null); - builder.forcedBreak(); - } - } - - @Override - protected List getPermitsClause(ClassTree node) { - return node.getPermitsClause(); - } - - @Override - public Void visitBindingPattern(BindingPatternTree node, Void unused) { - sync(node); - VariableTree variableTree = node.getVariable(); - visitBindingPattern( - variableTree.getModifiers(), variableTree.getType(), variableTree.getName()); - return null; - } - - private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { - declareOne( - DeclarationKind.PARAMETER, - Direction.HORIZONTAL, - Optional.of(modifiers), - type, - name, - /* op= */ "", - /* equals= */ "", - /* initializer= */ Optional.empty(), - /* trailing= */ Optional.empty(), - /* receiverExpression= */ Optional.empty(), - /* typeWithDims= */ Optional.empty()); - } - - @Override - public Void visitYield(YieldTree node, Void aVoid) { - sync(node); - token("yield"); - builder.space(); - scan(node.getValue(), null); - token(";"); - return null; - } - - @Override - public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { - sync(node); - visitSwitch(node.getExpression(), node.getCases()); - return null; - } - - @Override - public Void visitClass(ClassTree tree, Void unused) { - switch (tree.getKind()) { - case ANNOTATION_TYPE: - visitAnnotationType(tree); - break; - case CLASS: - case INTERFACE: - visitClassDeclaration(tree); - break; - case ENUM: - visitEnumDeclaration(tree); - break; - case RECORD: - visitRecordDeclaration(tree); - break; - default: - throw new AssertionError(tree.getKind()); - } - return null; - } - - public void visitRecordDeclaration(ClassTree node) { - sync(node); - typeDeclarationModifiers(node.getModifiers()); - Verify.verify(node.getExtendsClause() == null); - boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); - token("record"); - builder.space(); - visit(node.getSimpleName()); - if (!node.getTypeParameters().isEmpty()) { - token("<"); - } - builder.open(plusFour); - { - if (!node.getTypeParameters().isEmpty()) { - typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); - } - ImmutableList parameters = recordVariables(node); - token("("); - if (!parameters.isEmpty()) { - // Break before args. - builder.breakToFill(""); - } - // record headers can't declare receiver parameters - visitFormals(/* receiver= */ Optional.empty(), parameters); - token(")"); - if (hasSuperInterfaceTypes) { - builder.breakToFill(" "); - builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); - token("implements"); - builder.space(); - boolean afterFirstToken = false; - for (Tree superInterfaceType : node.getImplementsClause()) { - if (afterFirstToken) { - token(","); - builder.breakOp(" "); - } - scan(superInterfaceType, null); - afterFirstToken = true; - } - builder.close(); - } - } - builder.close(); - if (node.getMembers() == null) { - token(";"); - } else { - List members = - node.getMembers().stream() - .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) - .collect(toImmutableList()); - addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); - } - dropEmptyDeclarations(); - } - - private static ImmutableList recordVariables(ClassTree node) { - return node.getMembers().stream() - .filter(JCVariableDecl.class::isInstance) - .map(JCVariableDecl.class::cast) - .filter(m -> (m.mods.flags & RECORD) == RECORD) - .collect(toImmutableList()); - } - - @Override - public Void visitInstanceOf(InstanceOfTree node, Void unused) { - sync(node); - builder.open(plusFour); - scan(node.getExpression(), null); - builder.breakOp(" "); - builder.open(ZERO); - token("instanceof"); - builder.breakOp(" "); - if (node.getPattern() != null) { - scan(node.getPattern(), null); - } else { - scan(node.getType(), null); - } - builder.close(); - builder.close(); - return null; - } - - @Override - public Void visitCase(CaseTree node, Void unused) { - sync(node); - markForPartialFormat(); - builder.forcedBreak(); - List labels = node.getLabels(); - boolean isDefault = - labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL"); - builder.open(node.getCaseKind().equals(CaseTree.CaseKind.RULE) ? plusFour : ZERO); - if (isDefault) { - token("default", ZERO); - } else { - token("case", ZERO); - builder.open(ZERO); - builder.space(); - boolean afterFirstToken = false; - for (Tree expression : labels) { - if (afterFirstToken) { - token(","); - builder.breakOp(" "); - } - scan(expression, null); - afterFirstToken = true; - } - builder.close(); - } - - final ExpressionTree guard = getGuard(node); - if (guard != null) { - builder.breakToFill(" "); - token("when"); - builder.space(); - scan(guard, null); - } - - switch (node.getCaseKind()) { - case STATEMENT: - token(":"); - builder.open(plusTwo); - visitStatements(node.getStatements()); - builder.close(); - builder.close(); - break; - case RULE: - builder.space(); - token("-"); - token(">"); - if (node.getBody().getKind() == Tree.Kind.BLOCK) { - builder.close(); - builder.space(); - // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. - visitBlock( - (BlockTree) node.getBody(), - CollapseEmptyOrNot.YES, - AllowLeadingBlankLine.NO, - AllowTrailingBlankLine.NO); - } else { - builder.breakOp(" "); - scan(node.getBody(), null); - builder.close(); - } - builder.guessToken(";"); - break; - default: - throw new AssertionError(node.getCaseKind()); - } - return null; - } - - protected ExpressionTree getGuard(final CaseTree node) { - return null; - } -} 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 859c9c0cf..029da8e04 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 @@ -15,7 +15,7 @@ package com.google.googlejavaformat.java.java21; import com.google.googlejavaformat.OpsBuilder; -import com.google.googlejavaformat.java.java17.Java17InputAstVisitor; +import com.google.googlejavaformat.java.JavaInputAstVisitor; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ConstantCaseLabelTree; import com.sun.source.tree.DeconstructionPatternTree; @@ -28,10 +28,10 @@ import javax.lang.model.element.Name; /** - * Extends {@link Java17InputAstVisitor} with support for AST nodes that were added or modified in + * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in * Java 21. */ -public class Java21InputAstVisitor extends Java17InputAstVisitor { +public class Java21InputAstVisitor extends JavaInputAstVisitor { public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) { super(builder, indentMultiplier); diff --git a/core/src/main/resources/META-INF/native-image/reflect-config.json b/core/src/main/resources/META-INF/native-image/reflect-config.json index 4d30840f1..89577c035 100644 --- a/core/src/main/resources/META-INF/native-image/reflect-config.json +++ b/core/src/main/resources/META-INF/native-image/reflect-config.json @@ -3,15 +3,6 @@ "name": "com.sun.tools.javac.parser.UnicodeReader", "allDeclaredMethods": true }, - { - "name": "com.google.googlejavaformat.java.java17.Java17InputAstVisitor", - "methods": [ - { - "name": "", - "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"] - } - ] - }, { "name": "com.google.googlejavaformat.java.java21.Java21InputAstVisitor", "methods": [