diff --git a/.travis.yml b/.travis.yml
index 048e3ed19..b8a7c33af 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,7 @@ notifications:
jdk:
- openjdk11
+ - openjdk14
- openjdk-ea
matrix:
diff --git a/core/pom.xml b/core/pom.xml
index 6ec553fb0..c6f1e664b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -256,4 +256,32 @@
+
+
+
+ jdk11
+
+ (,14)
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ **/Java14InputAstVisitor.java
+
+
+
+
+ maven-javadoc-plugin
+
+ com.google.googlejavaformat.java.java14
+
+
+
+
+
+
diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
index 4348506b8..3e973958d 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
@@ -14,6 +14,8 @@
package com.google.googlejavaformat.java;
+import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
@@ -40,6 +42,7 @@
import com.sun.tools.javac.util.Options;
import java.io.IOError;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
@@ -115,6 +118,7 @@ static void format(final JavaInput javaInput, JavaOutput javaOutput, JavaFormatt
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
context.put(DiagnosticListener.class, diagnostics);
Options.instance(context).put("allowStringFolding", "false");
+ Options.instance(context).put("--enable-preview", "true");
JCCompilationUnit unit;
JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
try {
@@ -149,7 +153,21 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept
}
OpsBuilder builder = new OpsBuilder(javaInput, javaOutput);
// Output the compilation unit.
- new JavaInputAstVisitor(builder, options.indentationMultiplier()).scan(unit, null);
+ JavaInputAstVisitor visitor;
+ if (getMajor() >= 14) {
+ try {
+ visitor =
+ Class.forName("com.google.googlejavaformat.java.java14.Java14InputAstVisitor")
+ .asSubclass(JavaInputAstVisitor.class)
+ .getConstructor(OpsBuilder.class, int.class)
+ .newInstance(builder, options.indentationMultiplier());
+ } catch (ReflectiveOperationException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ } else {
+ visitor = new JavaInputAstVisitor(builder, options.indentationMultiplier());
+ }
+ visitor.scan(unit, null);
builder.sync(javaInput.getText().length());
builder.drain();
Doc doc = new DocBuilder().withOps(builder.build()).build();
@@ -158,6 +176,23 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept
javaOutput.flush();
}
+ // Runtime.Version was added in JDK 9, so use reflection to access it to preserve source
+ // compatibility with Java 8.
+ private static int getMajor() {
+ try {
+ Method versionMethod = Runtime.class.getMethod("version");
+ Object version = versionMethod.invoke(null);
+ return (int) version.getClass().getMethod("major").invoke(version);
+ } catch (Exception e) {
+ // continue below
+ }
+ int version = (int) Double.parseDouble(JAVA_CLASS_VERSION.value());
+ if (49 <= version && version <= 52) {
+ return version - (49 - 5);
+ }
+ throw new IllegalStateException("Unknown Java version: " + JAVA_SPECIFICATION_VERSION.value());
+ }
+
static boolean errorDiagnostic(Diagnostic> input) {
if (input.getKind() != Diagnostic.Kind.ERROR) {
return false;
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
index e5e4f4580..17ae0fac0 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
@@ -39,6 +39,7 @@
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
+import com.sun.tools.javac.util.Options;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
@@ -347,6 +348,7 @@ static ImmutableList buildToks(String text, ImmutableSet stopTok
throws FormatterException {
stopTokens = ImmutableSet.builder().addAll(stopTokens).add(TokenKind.EOF).build();
Context context = new Context();
+ Options.instance(context).put("--enable-preview", "true");
new JavacFileManager(context, true, UTF_8);
DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>();
context.put(DiagnosticListener.class, diagnosticCollector);
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
index 6ece007c7..11b9e3a0c 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
@@ -134,6 +134,7 @@
import com.sun.source.util.TreePathScanner;
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.TreeScanner;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -154,10 +155,10 @@
* An AST visitor that builds a stream of {@link Op}s to format from the given {@link
* CompilationUnitTree}.
*/
-public final class JavaInputAstVisitor extends TreePathScanner {
+public class JavaInputAstVisitor extends TreePathScanner {
/** Direction for Annotations (usually VERTICAL). */
- enum Direction {
+ protected enum Direction {
VERTICAL,
HORIZONTAL;
@@ -211,7 +212,7 @@ static AllowTrailingBlankLine valueOf(boolean b) {
}
/** Whether to include braces. */
- enum BracesOrNot {
+ protected enum BracesOrNot {
YES,
NO;
@@ -259,7 +260,7 @@ boolean isYes() {
}
/** Whether these declarations are the first in the block. */
- enum FirstDeclarationsOrNot {
+ protected enum FirstDeclarationsOrNot {
YES,
NO;
@@ -268,14 +269,14 @@ boolean isYes() {
}
}
- private final OpsBuilder builder;
+ protected final OpsBuilder builder;
- private static final Indent.Const ZERO = Indent.Const.ZERO;
- private final int indentMultiplier;
- private final Indent.Const minusTwo;
- private final Indent.Const minusFour;
- private final Indent.Const plusTwo;
- private final Indent.Const plusFour;
+ protected static final Indent.Const ZERO = Indent.Const.ZERO;
+ protected final int indentMultiplier;
+ protected final Indent.Const minusTwo;
+ protected final Indent.Const minusFour;
+ protected final Indent.Const plusTwo;
+ protected final Indent.Const plusFour;
private static final ImmutableList breakList(Optional breakTag) {
return ImmutableList.of(Doc.Break.make(Doc.FillMode.UNIFIED, " ", ZERO, breakTag));
@@ -382,7 +383,7 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) {
}
/** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */
- private void dropEmptyDeclarations() {
+ protected void dropEmptyDeclarations() {
if (builder.peekToken().equals(Optional.of(";"))) {
while (builder.peekToken().equals(Optional.of(";"))) {
builder.forcedBreak();
@@ -1331,12 +1332,19 @@ public Void visitAnnotatedType(AnnotatedTypeTree node, Void unused) {
return null;
}
+ // TODO(cushon): Use Flags.COMPACT_RECORD_CONSTRUCTOR once if/when we drop support for Java 11
+ protected static final long COMPACT_RECORD_CONSTRUCTOR = 1L << 51;
+
@Override
public Void visitMethod(MethodTree node, Void unused) {
sync(node);
List extends AnnotationTree> annotations = node.getModifiers().getAnnotations();
List extends AnnotationTree> returnTypeAnnotations = ImmutableList.of();
+ boolean isRecordConstructor =
+ (((JCMethodDecl) node).mods.flags & COMPACT_RECORD_CONSTRUCTOR)
+ == COMPACT_RECORD_CONSTRUCTOR;
+
if (!node.getTypeParameters().isEmpty() && !annotations.isEmpty()) {
int typeParameterStart = getStartPosition(node.getTypeParameters().get(0));
for (int i = 0; i < annotations.size(); i++) {
@@ -1406,7 +1414,9 @@ public Void visitMethod(MethodTree node, Void unused) {
name = builder.peekToken().get();
}
token(name);
- token("(");
+ if (!isRecordConstructor) {
+ token("(");
+ }
// end of name and type scope
builder.close();
}
@@ -1416,12 +1426,14 @@ public Void visitMethod(MethodTree node, Void unused) {
builder.open(Indent.If.make(breakBeforeType, plusFour, ZERO));
builder.open(ZERO);
{
- if (!node.getParameters().isEmpty() || node.getReceiverParameter() != null) {
- // Break before args.
- builder.breakToFill("");
- visitFormals(Optional.ofNullable(node.getReceiverParameter()), node.getParameters());
+ if (!isRecordConstructor) {
+ if (!node.getParameters().isEmpty() || node.getReceiverParameter() != null) {
+ // Break before args.
+ builder.breakToFill("");
+ visitFormals(Optional.ofNullable(node.getReceiverParameter()), node.getParameters());
+ }
+ token(")");
}
- token(")");
if (dims != null) {
maybeAddDims(dims);
}
@@ -1795,17 +1807,22 @@ public Void visitCase(CaseTree node, Void unused) {
@Override
public Void visitSwitch(SwitchTree node, Void unused) {
sync(node);
+ visitSwitch(node.getExpression(), node.getCases());
+ return null;
+ }
+
+ protected void visitSwitch(ExpressionTree expression, List extends CaseTree> cases) {
token("switch");
builder.space();
token("(");
- scan(skipParen(node.getExpression()), null);
+ scan(skipParen(expression), null);
token(")");
builder.space();
tokenBreakTrailingComment("{", plusTwo);
builder.blankLineWanted(BlankLineWanted.NO);
builder.open(plusTwo);
boolean first = true;
- for (CaseTree caseTree : node.getCases()) {
+ for (CaseTree caseTree : cases) {
if (!first) {
builder.blankLineWanted(BlankLineWanted.PRESERVE);
}
@@ -1816,7 +1833,6 @@ public Void visitSwitch(SwitchTree node, Void unused) {
builder.forcedBreak();
builder.blankLineWanted(BlankLineWanted.NO);
token("}", plusFour);
- return null;
}
@Override
@@ -2126,7 +2142,7 @@ private void visitStatement(
}
}
- private void visitStatements(List extends StatementTree> statements) {
+ protected void visitStatements(List extends StatementTree> statements) {
boolean first = true;
PeekingIterator it = Iterators.peekingIterator(statements.iterator());
dropEmptyDeclarations();
@@ -2164,7 +2180,7 @@ public Void visitModifiers(ModifiersTree node, Void unused) {
}
/** Output combined modifiers and annotations and returns the trailing break. */
- private List visitModifiers(
+ protected List visitModifiers(
ModifiersTree modifiersTree,
Direction annotationsDirection,
Optional declarationAnnotationBreak) {
@@ -2172,7 +2188,7 @@ private List visitModifiers(
modifiersTree.getAnnotations(), annotationsDirection, declarationAnnotationBreak);
}
- private List visitModifiers(
+ protected List visitModifiers(
List extends AnnotationTree> annotationTrees,
Direction annotationsDirection,
Optional declarationAnnotationBreak) {
@@ -2339,7 +2355,7 @@ private static void walkInfix(
}
}
- private void visitFormals(
+ protected void visitFormals(
Optional receiver, List extends VariableTree> parameters) {
if (!receiver.isPresent() && parameters.isEmpty()) {
return;
@@ -2559,7 +2575,7 @@ private void visitToDeclare(
}
/** Does not omit the leading '<', which should be associated with the type name. */
- private void typeParametersRest(
+ protected void typeParametersRest(
List extends TypeParameterTree> typeParameters, Indent plusIndent) {
builder.open(plusIndent);
builder.breakOp();
@@ -3446,7 +3462,7 @@ private void declareMany(List fragments, Direction annotationDirec
}
/** Add a list of declarations. */
- void addBodyDeclarations(
+ protected void addBodyDeclarations(
List extends Tree> bodyDeclarations, BracesOrNot braces, FirstDeclarationsOrNot first0) {
if (bodyDeclarations.isEmpty()) {
if (braces.isYes()) {
@@ -3592,7 +3608,7 @@ private Direction fieldAnnotationDirection(ModifiersTree modifiers) {
*
* @param token the {@link String} to wrap in a {@link Doc.Token}
*/
- final void token(String token) {
+ protected final void token(String token) {
builder.token(
token,
Doc.Token.RealOrImaginary.REAL,
@@ -3606,7 +3622,7 @@ final void token(String token) {
* @param token the {@link String} to wrap in a {@link Doc.Token}
* @param plusIndentCommentsBefore extra indent for comments before this token
*/
- final void token(String token, Indent plusIndentCommentsBefore) {
+ protected final void token(String token, Indent plusIndentCommentsBefore) {
builder.token(
token,
Doc.Token.RealOrImaginary.REAL,
@@ -3620,7 +3636,7 @@ final void tokenBreakTrailingComment(String token, Indent breakAndIndentTrailing
token, Doc.Token.RealOrImaginary.REAL, ZERO, Optional.of(breakAndIndentTrailingComment));
}
- private void markForPartialFormat() {
+ protected void markForPartialFormat() {
if (!inExpression()) {
builder.markForPartialFormat();
}
@@ -3632,7 +3648,7 @@ private void markForPartialFormat() {
*
* @param node the ASTNode holding the input position
*/
- final void sync(Tree node) {
+ protected final void sync(Tree node) {
builder.sync(((JCTree) node).getStartPosition());
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java
index 9d0bb27d2..d93948082 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java
@@ -200,6 +200,7 @@ private static JCCompilationUnit parse(Context context, String javaInput)
throws FormatterException {
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
context.put(DiagnosticListener.class, diagnostics);
+ Options.instance(context).put("--enable-preview", "true");
Options.instance(context).put("allowStringFolding", "false");
JCCompilationUnit unit;
JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
diff --git a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
index 6bb63db3a..e41bb6663 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
@@ -372,6 +372,7 @@ private static JCTree.JCCompilationUnit parse(String source, boolean allowString
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
Context context = new Context();
context.put(DiagnosticListener.class, diagnostics);
+ Options.instance(context).put("--enable-preview", "true");
Options.instance(context).put("allowStringFolding", Boolean.toString(allowStringFolding));
JCTree.JCCompilationUnit unit;
JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java
new file mode 100644
index 000000000..f3dee6b67
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java
@@ -0,0 +1,219 @@
+/*
+ * 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.onlyElement;
+
+import com.google.common.base.Verify;
+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.MethodTree;
+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.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 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);
+ }
+ MethodTree constructor =
+ node.getMembers().stream()
+ .filter(JCMethodDecl.class::isInstance)
+ .map(JCMethodDecl.class::cast)
+ .filter(
+ m -> (m.mods.flags & COMPACT_RECORD_CONSTRUCTOR) == COMPACT_RECORD_CONSTRUCTOR)
+ .collect(onlyElement());
+ token("(");
+ if (!constructor.getParameters().isEmpty() || constructor.getReceiverParameter() != null) {
+ // Break before args.
+ builder.breakToFill("");
+ }
+ visitFormals(
+ Optional.ofNullable(constructor.getReceiverParameter()), constructor.getParameters());
+ 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 members =
+ node.getMembers().stream()
+ .filter(t -> (TreeInfo.flags((JCTree) t) & Flags.GENERATED_MEMBER) == 0)
+ .collect(toImmutableList());
+ addBodyDeclarations(members, BracesOrNot.YES, FirstDeclarationsOrNot.YES);
+ }
+ dropEmptyDeclarations();
+ }
+
+ @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;
+ }
+}
diff --git a/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java b/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java
index 58ea959fd..0b81ba6b4 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java
@@ -143,8 +143,7 @@ public void oneFileParseErrorReplace() throws Exception {
int result = main.format("-i", pathOne.toString(), pathTwo.toString());
assertThat(stdout.toString()).isEmpty();
- assertThat(stderr.toString())
- .contains("One.java:1:14: error: class, interface, or enum expected");
+ assertThat(stderr.toString()).contains("One.java:1:14: error: class, interface");
assertThat(result).isEqualTo(1);
// don't edit files with parse errors
assertThat(Files.readAllLines(pathOne, UTF_8)).containsExactly("class One {}}");
diff --git a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
index bb722f2ea..289ea1baf 100644
--- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
+++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java
@@ -14,6 +14,8 @@
package com.google.googlejavaformat.java;
+import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static com.google.common.io.Files.getFileExtension;
import static com.google.common.io.Files.getNameWithoutExtension;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -21,6 +23,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.google.common.collect.ImmutableSet;
import com.google.common.io.CharStreams;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ResourceInfo;
@@ -28,6 +31,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.lang.reflect.Method;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -43,6 +47,8 @@
@RunWith(Parameterized.class)
public class FormatterIntegrationTest {
+ private static final ImmutableSet JAVA14_TESTS = ImmutableSet.of("java14");
+
@Parameters(name = "{index}: {0}")
public static Iterable