aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java')
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java297
1 files changed, 297 insertions, 0 deletions
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
new file mode 100644
index 0000000..6818f4a
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
@@ -0,0 +1,297 @@
+/*
+ * 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.AnnotationTree;
+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 first, CompilationUnitTree node) {
+ ModuleTree module = node.getModule();
+ if (module != null) {
+ if (!first) {
+ builder.blankLineWanted(BlankLineWanted.YES);
+ }
+ markForPartialFormat();
+ visitModule(module, null);
+ builder.forcedBreak();
+ }
+ }
+
+ @Override
+ protected List<? extends Tree> 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) {
+ builder.open(plusFour);
+ if (modifiers != null) {
+ List<AnnotationTree> annotations =
+ visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty());
+ visitAnnotations(annotations, BreakOrNot.NO, BreakOrNot.YES);
+ }
+ scan(type, null);
+ builder.breakOp(" ");
+ if (name.isEmpty()) {
+ token("_");
+ } else {
+ visit(name);
+ }
+ builder.close();
+ }
+
+ @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<JCVariableDecl> 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 first = true;
+ for (Tree superInterfaceType : node.getImplementsClause()) {
+ if (!first) {
+ token(",");
+ builder.breakOp(" ");
+ }
+ scan(superInterfaceType, null);
+ first = false;
+ }
+ builder.close();
+ }
+ }
+ builder.close();
+ if (node.getMembers() == null) {
+ token(";");
+ } else {
+ List<Tree> 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<JCVariableDecl> 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<? extends CaseLabelTree> labels = node.getLabels();
+ boolean isDefault =
+ labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL");
+ builder.open(
+ node.getCaseKind().equals(CaseTree.CaseKind.RULE)
+ && !node.getBody().getKind().equals(Tree.Kind.BLOCK)
+ ? plusFour
+ : ZERO);
+ if (isDefault) {
+ token("default", plusTwo);
+ } else {
+ token("case", plusTwo);
+ builder.open(labels.size() > 1 ? plusFour : ZERO);
+ builder.space();
+ boolean first = true;
+ for (Tree expression : labels) {
+ if (!first) {
+ token(",");
+ builder.breakOp(" ");
+ }
+ scan(expression, null);
+ first = false;
+ }
+ builder.close();
+ }
+
+ final ExpressionTree guard = getGuard(node);
+ if (guard != null) {
+ builder.space();
+ token("when");
+ builder.space();
+ scan(guard, null);
+ }
+
+ switch (node.getCaseKind()) {
+ case STATEMENT:
+ token(":");
+ builder.open(plusTwo);
+ visitStatements(node.getStatements());
+ builder.close();
+ break;
+ case RULE:
+ builder.space();
+ token("-");
+ token(">");
+ if (node.getBody().getKind() == Tree.Kind.BLOCK) {
+ 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.guessToken(";");
+ break;
+ default:
+ throw new AssertionError(node.getCaseKind());
+ }
+ builder.close();
+ return null;
+ }
+
+ protected ExpressionTree getGuard(final CaseTree node) {
+ return null;
+ }
+}