1 /* 2 * Copyright 2020 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.googlejavaformat.java.java14; 16 17 import static com.google.common.collect.ImmutableList.toImmutableList; 18 import static com.google.common.collect.MoreCollectors.toOptional; 19 20 import com.google.common.base.Verify; 21 import com.google.common.collect.ImmutableList; 22 import com.google.googlejavaformat.Op; 23 import com.google.googlejavaformat.OpsBuilder; 24 import com.google.googlejavaformat.java.JavaInputAstVisitor; 25 import com.sun.source.tree.BindingPatternTree; 26 import com.sun.source.tree.CaseTree; 27 import com.sun.source.tree.ClassTree; 28 import com.sun.source.tree.ExpressionTree; 29 import com.sun.source.tree.InstanceOfTree; 30 import com.sun.source.tree.SwitchExpressionTree; 31 import com.sun.source.tree.Tree; 32 import com.sun.source.tree.YieldTree; 33 import com.sun.tools.javac.code.Flags; 34 import com.sun.tools.javac.tree.JCTree; 35 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 36 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 37 import com.sun.tools.javac.tree.TreeInfo; 38 import java.util.List; 39 import java.util.Optional; 40 41 /** 42 * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for 43 * Java 14. 44 */ 45 public class Java14InputAstVisitor extends JavaInputAstVisitor { 46 Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier)47 public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) { 48 super(builder, indentMultiplier); 49 } 50 51 @Override visitBindingPattern(BindingPatternTree node, Void unused)52 public Void visitBindingPattern(BindingPatternTree node, Void unused) { 53 sync(node); 54 scan(node.getType(), null); 55 builder.breakOp(" "); 56 visit(node.getBinding()); 57 return null; 58 } 59 60 @Override visitYield(YieldTree node, Void aVoid)61 public Void visitYield(YieldTree node, Void aVoid) { 62 sync(node); 63 token("yield"); 64 builder.space(); 65 scan(node.getValue(), null); 66 token(";"); 67 return null; 68 } 69 70 @Override visitSwitchExpression(SwitchExpressionTree node, Void aVoid)71 public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { 72 sync(node); 73 visitSwitch(node.getExpression(), node.getCases()); 74 return null; 75 } 76 77 @Override visitClass(ClassTree tree, Void unused)78 public Void visitClass(ClassTree tree, Void unused) { 79 switch (tree.getKind()) { 80 case ANNOTATION_TYPE: 81 visitAnnotationType(tree); 82 break; 83 case CLASS: 84 case INTERFACE: 85 visitClassDeclaration(tree); 86 break; 87 case ENUM: 88 visitEnumDeclaration(tree); 89 break; 90 case RECORD: 91 visitRecordDeclaration(tree); 92 break; 93 default: 94 throw new AssertionError(tree.getKind()); 95 } 96 return null; 97 } 98 visitRecordDeclaration(ClassTree node)99 public void visitRecordDeclaration(ClassTree node) { 100 sync(node); 101 List<Op> breaks = 102 visitModifiers( 103 node.getModifiers(), 104 Direction.VERTICAL, 105 /* declarationAnnotationBreak= */ Optional.empty()); 106 Verify.verify(node.getExtendsClause() == null); 107 boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); 108 builder.addAll(breaks); 109 token("record"); 110 builder.space(); 111 visit(node.getSimpleName()); 112 if (!node.getTypeParameters().isEmpty()) { 113 token("<"); 114 } 115 builder.open(plusFour); 116 { 117 if (!node.getTypeParameters().isEmpty()) { 118 typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); 119 } 120 ImmutableList<JCVariableDecl> parameters = 121 compactRecordConstructor(node) 122 .map(m -> ImmutableList.copyOf(m.getParameters())) 123 .orElseGet(() -> recordVariables(node)); 124 token("("); 125 if (!parameters.isEmpty()) { 126 // Break before args. 127 builder.breakToFill(""); 128 } 129 // record headers can't declare receiver parameters 130 visitFormals(/* receiver= */ Optional.empty(), parameters); 131 token(")"); 132 if (hasSuperInterfaceTypes) { 133 builder.breakToFill(" "); 134 builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); 135 token("implements"); 136 builder.space(); 137 boolean first = true; 138 for (Tree superInterfaceType : node.getImplementsClause()) { 139 if (!first) { 140 token(","); 141 builder.breakOp(" "); 142 } 143 scan(superInterfaceType, null); 144 first = false; 145 } 146 builder.close(); 147 } 148 } 149 builder.close(); 150 if (node.getMembers() == null) { 151 token(";"); 152 } else { 153 List<Tree> members = 154 node.getMembers().stream() 155 .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) 156 .collect(toImmutableList()); 157 addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); 158 } 159 dropEmptyDeclarations(); 160 } 161 compactRecordConstructor(ClassTree node)162 private static Optional<JCMethodDecl> compactRecordConstructor(ClassTree node) { 163 return node.getMembers().stream() 164 .filter(JCMethodDecl.class::isInstance) 165 .map(JCMethodDecl.class::cast) 166 .filter(m -> (m.mods.flags & COMPACT_RECORD_CONSTRUCTOR) == COMPACT_RECORD_CONSTRUCTOR) 167 .collect(toOptional()); 168 } 169 recordVariables(ClassTree node)170 private static ImmutableList<JCVariableDecl> recordVariables(ClassTree node) { 171 return node.getMembers().stream() 172 .filter(JCVariableDecl.class::isInstance) 173 .map(JCVariableDecl.class::cast) 174 .filter(m -> (m.mods.flags & RECORD) == RECORD) 175 .collect(toImmutableList()); 176 } 177 178 @Override visitInstanceOf(InstanceOfTree node, Void unused)179 public Void visitInstanceOf(InstanceOfTree node, Void unused) { 180 sync(node); 181 builder.open(plusFour); 182 scan(node.getExpression(), null); 183 builder.breakOp(" "); 184 builder.open(ZERO); 185 token("instanceof"); 186 builder.breakOp(" "); 187 if (node.getPattern() != null) { 188 scan(node.getPattern(), null); 189 } else { 190 scan(node.getType(), null); 191 } 192 builder.close(); 193 builder.close(); 194 return null; 195 } 196 197 @Override visitCase(CaseTree node, Void unused)198 public Void visitCase(CaseTree node, Void unused) { 199 sync(node); 200 markForPartialFormat(); 201 builder.forcedBreak(); 202 if (node.getExpressions().isEmpty()) { 203 token("default", plusTwo); 204 } else { 205 token("case", plusTwo); 206 builder.space(); 207 boolean first = true; 208 for (ExpressionTree expression : node.getExpressions()) { 209 if (!first) { 210 token(","); 211 builder.space(); 212 } 213 scan(expression, null); 214 first = false; 215 } 216 } 217 switch (node.getCaseKind()) { 218 case STATEMENT: 219 token(":"); 220 builder.open(plusTwo); 221 visitStatements(node.getStatements()); 222 builder.close(); 223 break; 224 case RULE: 225 builder.space(); 226 token("-"); 227 token(">"); 228 builder.space(); 229 scan(node.getBody(), null); 230 builder.guessToken(";"); 231 break; 232 default: 233 throw new AssertionError(node.getCaseKind()); 234 } 235 return null; 236 } 237 } 238