blob: 28a110395f903c784d37507e9ebbd97973134cda [file] [log] [blame]
/*
* 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.java14;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.toOptional;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import com.google.googlejavaformat.Op;
import com.google.googlejavaformat.OpsBuilder;
import com.google.googlejavaformat.java.JavaInputAstVisitor;
import com.sun.source.tree.BindingPatternTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.SwitchExpressionTree;
import com.sun.source.tree.Tree;
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.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeInfo;
import java.util.List;
import java.util.Optional;
/**
* Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for
* Java 14.
*/
public class Java14InputAstVisitor extends JavaInputAstVisitor {
public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
super(builder, indentMultiplier);
}
@Override
public Void visitBindingPattern(BindingPatternTree node, Void unused) {
sync(node);
scan(node.getType(), null);
builder.breakOp(" ");
visit(node.getBinding());
return null;
}
@Override
public Void visitYield(YieldTree node, Void aVoid) {
sync(node);
return super.visitYield(node, aVoid);
}
@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);
List<Op> breaks =
visitModifiers(
node.getModifiers(),
Direction.VERTICAL,
/* declarationAnnotationBreak= */ Optional.empty());
Verify.verify(node.getExtendsClause() == null);
boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty();
builder.addAll(breaks);
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 =
compactRecordConstructor(node)
.map(m -> ImmutableList.copyOf(m.getParameters()))
.orElseGet(() -> 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 Optional<JCMethodDecl> compactRecordConstructor(ClassTree node) {
return node.getMembers().stream()
.filter(JCMethodDecl.class::isInstance)
.map(JCMethodDecl.class::cast)
.filter(m -> (m.mods.flags & COMPACT_RECORD_CONSTRUCTOR) == COMPACT_RECORD_CONSTRUCTOR)
.collect(toOptional());
}
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();
if (node.getExpressions().isEmpty()) {
token("default", plusTwo);
} else {
token("case", plusTwo);
builder.space();
boolean first = true;
for (ExpressionTree expression : node.getExpressions()) {
if (!first) {
token(",");
builder.space();
}
scan(expression, null);
first = false;
}
}
switch (node.getCaseKind()) {
case STATEMENT:
token(":");
builder.open(plusTwo);
visitStatements(node.getStatements());
builder.close();
break;
case RULE:
builder.space();
token("-");
token(">");
builder.space();
scan(node.getBody(), null);
token(";");
break;
default:
throw new AssertionError(node.getCaseKind());
}
return null;
}
}