• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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