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.java17; 16 17 import static com.google.common.collect.ImmutableList.toImmutableList; 18 import static com.google.common.collect.Iterables.getOnlyElement; 19 20 import com.google.common.base.Verify; 21 import com.google.common.collect.ImmutableList; 22 import com.google.googlejavaformat.OpsBuilder; 23 import com.google.googlejavaformat.OpsBuilder.BlankLineWanted; 24 import com.google.googlejavaformat.java.JavaInputAstVisitor; 25 import com.sun.source.tree.AnnotationTree; 26 import com.sun.source.tree.BindingPatternTree; 27 import com.sun.source.tree.BlockTree; 28 import com.sun.source.tree.CaseLabelTree; 29 import com.sun.source.tree.CaseTree; 30 import com.sun.source.tree.ClassTree; 31 import com.sun.source.tree.CompilationUnitTree; 32 import com.sun.source.tree.ExpressionTree; 33 import com.sun.source.tree.InstanceOfTree; 34 import com.sun.source.tree.ModifiersTree; 35 import com.sun.source.tree.ModuleTree; 36 import com.sun.source.tree.SwitchExpressionTree; 37 import com.sun.source.tree.Tree; 38 import com.sun.source.tree.VariableTree; 39 import com.sun.source.tree.YieldTree; 40 import com.sun.tools.javac.code.Flags; 41 import com.sun.tools.javac.tree.JCTree; 42 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 43 import com.sun.tools.javac.tree.TreeInfo; 44 import java.util.List; 45 import java.util.Optional; 46 import javax.lang.model.element.Name; 47 48 /** 49 * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in 50 * Java 17. 51 */ 52 public class Java17InputAstVisitor extends JavaInputAstVisitor { 53 Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier)54 public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) { 55 super(builder, indentMultiplier); 56 } 57 58 @Override handleModule(boolean first, CompilationUnitTree node)59 protected void handleModule(boolean first, CompilationUnitTree node) { 60 ModuleTree module = node.getModule(); 61 if (module != null) { 62 if (!first) { 63 builder.blankLineWanted(BlankLineWanted.YES); 64 } 65 markForPartialFormat(); 66 visitModule(module, null); 67 builder.forcedBreak(); 68 } 69 } 70 71 @Override getPermitsClause(ClassTree node)72 protected List<? extends Tree> getPermitsClause(ClassTree node) { 73 return node.getPermitsClause(); 74 } 75 76 @Override visitBindingPattern(BindingPatternTree node, Void unused)77 public Void visitBindingPattern(BindingPatternTree node, Void unused) { 78 sync(node); 79 VariableTree variableTree = node.getVariable(); 80 visitBindingPattern( 81 variableTree.getModifiers(), variableTree.getType(), variableTree.getName()); 82 return null; 83 } 84 visitBindingPattern(ModifiersTree modifiers, Tree type, Name name)85 private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { 86 builder.open(plusFour); 87 if (modifiers != null) { 88 List<AnnotationTree> annotations = 89 visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty()); 90 visitAnnotations(annotations, BreakOrNot.NO, BreakOrNot.YES); 91 } 92 scan(type, null); 93 builder.breakOp(" "); 94 if (name.isEmpty()) { 95 token("_"); 96 } else { 97 visit(name); 98 } 99 builder.close(); 100 } 101 102 @Override visitYield(YieldTree node, Void aVoid)103 public Void visitYield(YieldTree node, Void aVoid) { 104 sync(node); 105 token("yield"); 106 builder.space(); 107 scan(node.getValue(), null); 108 token(";"); 109 return null; 110 } 111 112 @Override visitSwitchExpression(SwitchExpressionTree node, Void aVoid)113 public Void visitSwitchExpression(SwitchExpressionTree node, Void aVoid) { 114 sync(node); 115 visitSwitch(node.getExpression(), node.getCases()); 116 return null; 117 } 118 119 @Override visitClass(ClassTree tree, Void unused)120 public Void visitClass(ClassTree tree, Void unused) { 121 switch (tree.getKind()) { 122 case ANNOTATION_TYPE: 123 visitAnnotationType(tree); 124 break; 125 case CLASS: 126 case INTERFACE: 127 visitClassDeclaration(tree); 128 break; 129 case ENUM: 130 visitEnumDeclaration(tree); 131 break; 132 case RECORD: 133 visitRecordDeclaration(tree); 134 break; 135 default: 136 throw new AssertionError(tree.getKind()); 137 } 138 return null; 139 } 140 visitRecordDeclaration(ClassTree node)141 public void visitRecordDeclaration(ClassTree node) { 142 sync(node); 143 typeDeclarationModifiers(node.getModifiers()); 144 Verify.verify(node.getExtendsClause() == null); 145 boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); 146 token("record"); 147 builder.space(); 148 visit(node.getSimpleName()); 149 if (!node.getTypeParameters().isEmpty()) { 150 token("<"); 151 } 152 builder.open(plusFour); 153 { 154 if (!node.getTypeParameters().isEmpty()) { 155 typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); 156 } 157 ImmutableList<JCVariableDecl> parameters = recordVariables(node); 158 token("("); 159 if (!parameters.isEmpty()) { 160 // Break before args. 161 builder.breakToFill(""); 162 } 163 // record headers can't declare receiver parameters 164 visitFormals(/* receiver= */ Optional.empty(), parameters); 165 token(")"); 166 if (hasSuperInterfaceTypes) { 167 builder.breakToFill(" "); 168 builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); 169 token("implements"); 170 builder.space(); 171 boolean first = true; 172 for (Tree superInterfaceType : node.getImplementsClause()) { 173 if (!first) { 174 token(","); 175 builder.breakOp(" "); 176 } 177 scan(superInterfaceType, null); 178 first = false; 179 } 180 builder.close(); 181 } 182 } 183 builder.close(); 184 if (node.getMembers() == null) { 185 token(";"); 186 } else { 187 List<Tree> members = 188 node.getMembers().stream() 189 .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0) 190 .collect(toImmutableList()); 191 addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES); 192 } 193 dropEmptyDeclarations(); 194 } 195 recordVariables(ClassTree node)196 private static ImmutableList<JCVariableDecl> recordVariables(ClassTree node) { 197 return node.getMembers().stream() 198 .filter(JCVariableDecl.class::isInstance) 199 .map(JCVariableDecl.class::cast) 200 .filter(m -> (m.mods.flags & RECORD) == RECORD) 201 .collect(toImmutableList()); 202 } 203 204 @Override visitInstanceOf(InstanceOfTree node, Void unused)205 public Void visitInstanceOf(InstanceOfTree node, Void unused) { 206 sync(node); 207 builder.open(plusFour); 208 scan(node.getExpression(), null); 209 builder.breakOp(" "); 210 builder.open(ZERO); 211 token("instanceof"); 212 builder.breakOp(" "); 213 if (node.getPattern() != null) { 214 scan(node.getPattern(), null); 215 } else { 216 scan(node.getType(), null); 217 } 218 builder.close(); 219 builder.close(); 220 return null; 221 } 222 223 @Override visitCase(CaseTree node, Void unused)224 public Void visitCase(CaseTree node, Void unused) { 225 sync(node); 226 markForPartialFormat(); 227 builder.forcedBreak(); 228 List<? extends CaseLabelTree> labels = node.getLabels(); 229 boolean isDefault = 230 labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL"); 231 builder.open( 232 node.getCaseKind().equals(CaseTree.CaseKind.RULE) 233 && !node.getBody().getKind().equals(Tree.Kind.BLOCK) 234 ? plusFour 235 : ZERO); 236 if (isDefault) { 237 token("default", plusTwo); 238 } else { 239 token("case", plusTwo); 240 builder.open(labels.size() > 1 ? plusFour : ZERO); 241 builder.space(); 242 boolean first = true; 243 for (Tree expression : labels) { 244 if (!first) { 245 token(","); 246 builder.breakOp(" "); 247 } 248 scan(expression, null); 249 first = false; 250 } 251 builder.close(); 252 } 253 254 final ExpressionTree guard = getGuard(node); 255 if (guard != null) { 256 builder.space(); 257 token("when"); 258 builder.space(); 259 scan(guard, null); 260 } 261 262 switch (node.getCaseKind()) { 263 case STATEMENT: 264 token(":"); 265 builder.open(plusTwo); 266 visitStatements(node.getStatements()); 267 builder.close(); 268 break; 269 case RULE: 270 builder.space(); 271 token("-"); 272 token(">"); 273 if (node.getBody().getKind() == Tree.Kind.BLOCK) { 274 builder.space(); 275 // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. 276 visitBlock( 277 (BlockTree) node.getBody(), 278 CollapseEmptyOrNot.YES, 279 AllowLeadingBlankLine.NO, 280 AllowTrailingBlankLine.NO); 281 } else { 282 builder.breakOp(" "); 283 scan(node.getBody(), null); 284 } 285 builder.guessToken(";"); 286 break; 287 default: 288 throw new AssertionError(node.getCaseKind()); 289 } 290 builder.close(); 291 return null; 292 } 293 getGuard(final CaseTree node)294 protected ExpressionTree getGuard(final CaseTree node) { 295 return null; 296 } 297 } 298