aboutsummaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/com/google/googlejavaformat/CommentsHelper.java19
-rw-r--r--core/src/main/java/com/google/googlejavaformat/Doc.java34
-rw-r--r--core/src/main/java/com/google/googlejavaformat/OpsBuilder.java2
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java57
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/Formatter.java40
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java53
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java5
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java10
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java50
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaInput.java17
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java19
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java2
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java52
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/Main.java102
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java28
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java11
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java (renamed from core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java)109
-rw-r--r--core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java101
-rw-r--r--core/src/main/resources/META-INF/native-image/reflect-config.json18
19 files changed, 502 insertions, 227 deletions
diff --git a/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java b/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
index 45e507b..1e33003 100644
--- a/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
+++ b/core/src/main/java/com/google/googlejavaformat/CommentsHelper.java
@@ -14,6 +14,10 @@
package com.google.googlejavaformat;
+import com.google.googlejavaformat.Input.Tok;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
/**
* Rewrite comments. This interface is implemented by {@link
* com.google.googlejavaformat.java.JavaCommentsHelper JavaCommentsHelper}.
@@ -28,4 +32,19 @@ public interface CommentsHelper {
* @return the rewritten comment
*/
String rewrite(Input.Tok tok, int maxWidth, int column0);
+
+ static Optional<String> reformatParameterComment(Tok tok) {
+ if (!tok.isSlashStarComment()) {
+ return Optional.empty();
+ }
+ var match = PARAMETER_COMMENT.matcher(tok.getOriginalText());
+ if (!match.matches()) {
+ return Optional.empty();
+ }
+ return Optional.of(String.format("/* %s= */", match.group(1)));
+ }
+
+ Pattern PARAMETER_COMMENT =
+ Pattern.compile(
+ "/\\*\\s*(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\Q...\\E)?)\\s*=\\s*\\*/");
}
diff --git a/core/src/main/java/com/google/googlejavaformat/Doc.java b/core/src/main/java/com/google/googlejavaformat/Doc.java
index 35acca3..cab6885 100644
--- a/core/src/main/java/com/google/googlejavaformat/Doc.java
+++ b/core/src/main/java/com/google/googlejavaformat/Doc.java
@@ -15,9 +15,12 @@
package com.google.googlejavaformat;
import static com.google.common.collect.Iterables.getLast;
+import static com.google.googlejavaformat.CommentsHelper.reformatParameterComment;
import static java.lang.Math.max;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
@@ -101,16 +104,13 @@ public abstract class Doc {
private static final DiscreteDomain<Integer> INTEGERS = DiscreteDomain.integers();
// Memoized width; Float.POSITIVE_INFINITY if contains forced breaks.
- private boolean widthComputed = false;
- private float width = 0.0F;
+ private final Supplier<Float> width = Suppliers.memoize(this::computeWidth);
// Memoized flat; not defined (and never computed) if contains forced breaks.
- private boolean flatComputed = false;
- private String flat = "";
+ private final Supplier<String> flat = Suppliers.memoize(this::computeFlat);
// Memoized Range.
- private boolean rangeComputed = false;
- private Range<Integer> range = EMPTY_RANGE;
+ private final Supplier<Range<Integer>> range = Suppliers.memoize(this::computeRange);
/**
* Return the width of a {@code Doc}, or {@code Float.POSITIVE_INFINITY} if it must be broken.
@@ -118,11 +118,7 @@ public abstract class Doc {
* @return the width
*/
final float getWidth() {
- if (!widthComputed) {
- width = computeWidth();
- widthComputed = true;
- }
- return width;
+ return width.get();
}
/**
@@ -132,11 +128,7 @@ public abstract class Doc {
* @return the flat-string value
*/
final String getFlat() {
- if (!flatComputed) {
- flat = computeFlat();
- flatComputed = true;
- }
- return flat;
+ return flat.get();
}
/**
@@ -145,11 +137,7 @@ public abstract class Doc {
* @return the {@code Doc}'s {@link Range}
*/
final Range<Integer> range() {
- if (!rangeComputed) {
- range = computeRange();
- rangeComputed = true;
- }
- return range;
+ return range.get();
}
/**
@@ -727,7 +715,7 @@ public abstract class Doc {
// Account for line comments with missing spaces, see computeFlat.
return tok.length() + 1;
} else {
- return tok.length();
+ return reformatParameterComment(tok).map(String::length).orElse(tok.length());
}
}
return idx != -1 ? Float.POSITIVE_INFINITY : (float) tok.length();
@@ -741,7 +729,7 @@ public abstract class Doc {
if (tok.isSlashSlashComment() && !tok.getOriginalText().startsWith("// ")) {
return "// " + tok.getOriginalText().substring("//".length());
}
- return tok.getOriginalText();
+ return reformatParameterComment(tok).orElse(tok.getOriginalText());
}
@Override
diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java
index db431c0..a45e83b 100644
--- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java
+++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java
@@ -159,7 +159,7 @@ public final class OpsBuilder {
int depth = 0;
/** Add an {@link Op}, and record open/close ops for later validation of unclosed levels. */
- private void add(Op op) {
+ public final void add(Op op) {
if (op instanceof OpenOp) {
depth++;
} else if (op instanceof CloseOp) {
diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
index 9d8ae41..3d68a23 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/FormatFileCallable.java
@@ -14,40 +14,73 @@
package com.google.googlejavaformat.java;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
+import java.nio.file.Path;
import java.util.concurrent.Callable;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Encapsulates information about a file to be formatted, including which parts of the file to
* format.
*/
-class FormatFileCallable implements Callable<String> {
+class FormatFileCallable implements Callable<FormatFileCallable.Result> {
+
+ @AutoValue
+ abstract static class Result {
+ abstract @Nullable Path path();
+
+ abstract String input();
+
+ abstract @Nullable String output();
+
+ boolean changed() {
+ return !input().equals(output());
+ }
+
+ abstract @Nullable FormatterException exception();
+
+ static Result create(
+ @Nullable Path path,
+ String input,
+ @Nullable String output,
+ @Nullable FormatterException exception) {
+ return new AutoValue_FormatFileCallable_Result(path, input, output, exception);
+ }
+ }
+
+ private final Path path;
private final String input;
private final CommandLineOptions parameters;
private final JavaFormatterOptions options;
public FormatFileCallable(
- CommandLineOptions parameters, String input, JavaFormatterOptions options) {
+ CommandLineOptions parameters, Path path, String input, JavaFormatterOptions options) {
+ this.path = path;
this.input = input;
this.parameters = parameters;
this.options = options;
}
@Override
- public String call() throws FormatterException {
- if (parameters.fixImportsOnly()) {
- return fixImports(input);
- }
+ public Result call() {
+ try {
+ if (parameters.fixImportsOnly()) {
+ return Result.create(path, input, fixImports(input), /* exception= */ null);
+ }
- Formatter formatter = new Formatter(options);
- String formatted = formatter.formatSource(input, characterRanges(input).asRanges());
- formatted = fixImports(formatted);
- if (parameters.reflowLongStrings()) {
- formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter);
+ Formatter formatter = new Formatter(options);
+ String formatted = formatter.formatSource(input, characterRanges(input).asRanges());
+ formatted = fixImports(formatted);
+ if (parameters.reflowLongStrings()) {
+ formatted = StringWrapper.wrap(Formatter.MAX_LINE_LENGTH, formatted, formatter);
+ }
+ return Result.create(path, input, formatted, /* exception= */ null);
+ } catch (FormatterException e) {
+ return Result.create(path, input, /* output= */ null, e);
}
- return formatted;
}
private String fixImports(String input) throws FormatterException {
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 aac829d..5aa7a12 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java
@@ -136,9 +136,9 @@ public final class Formatter {
JavacParser parser =
parserFactory.newParser(
javaInput.getText(),
- /*keepDocComments=*/ true,
- /*keepEndPos=*/ true,
- /*keepLineMap=*/ true);
+ /* keepDocComments= */ true,
+ /* keepEndPos= */ true,
+ /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = source;
@@ -151,16 +151,14 @@ public final class Formatter {
OpsBuilder builder = new OpsBuilder(javaInput, javaOutput);
// Output the compilation unit.
JavaInputAstVisitor visitor;
- if (Runtime.version().feature() >= 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);
- }
+ if (Runtime.version().feature() >= 21) {
+ visitor =
+ createVisitor(
+ "com.google.googlejavaformat.java.java21.Java21InputAstVisitor", builder, options);
+ } else if (Runtime.version().feature() >= 17) {
+ visitor =
+ createVisitor(
+ "com.google.googlejavaformat.java.java17.Java17InputAstVisitor", builder, options);
} else {
visitor = new JavaInputAstVisitor(builder, options.indentationMultiplier());
}
@@ -173,6 +171,18 @@ public final class Formatter {
javaOutput.flush();
}
+ private static JavaInputAstVisitor createVisitor(
+ final String className, final OpsBuilder builder, final JavaFormatterOptions options) {
+ try {
+ return Class.forName(className)
+ .asSubclass(JavaInputAstVisitor.class)
+ .getConstructor(OpsBuilder.class, int.class)
+ .newInstance(builder, options.indentationMultiplier());
+ } catch (ReflectiveOperationException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ }
+
static boolean errorDiagnostic(Diagnostic<?> input) {
if (input.getKind() != Diagnostic.Kind.ERROR) {
return false;
@@ -262,7 +272,9 @@ public final class Formatter {
// TODO(cushon): this is only safe because the modifier ordering doesn't affect whitespace,
// and doesn't change the replacements that are output. This is not true in general for
// 'de-linting' changes (e.g. import ordering).
- javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges);
+ if (options.reorderModifiers()) {
+ javaInput = ModifierOrderer.reorderModifiers(javaInput, characterRanges);
+ }
String lineSeparator = Newlines.guessLineSeparator(input);
JavaOutput javaOutput =
diff --git a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java
new file mode 100644
index 0000000..3c315aa
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatTool.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 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;
+
+import static com.google.common.collect.Sets.toImmutableEnumSet;
+
+import com.google.auto.service.AutoService;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Set;
+import javax.lang.model.SourceVersion;
+import javax.tools.Tool;
+
+/** Provide a way to be invoked without necessarily starting a new VM. */
+@AutoService(Tool.class)
+public class GoogleJavaFormatTool implements Tool {
+ @Override
+ public String name() {
+ return "google-java-format";
+ }
+
+ @Override
+ public Set<SourceVersion> getSourceVersions() {
+ return Arrays.stream(SourceVersion.values()).collect(toImmutableEnumSet());
+ }
+
+ @Override
+ public int run(InputStream in, OutputStream out, OutputStream err, String... args) {
+ PrintStream outStream = new PrintStream(out);
+ PrintStream errStream = new PrintStream(err);
+ try {
+ return Main.main(in, outStream, errStream, args);
+ } catch (RuntimeException e) {
+ errStream.print(e.getMessage());
+ errStream.flush();
+ return 1; // pass non-zero value back indicating an error has happened
+ }
+ }
+}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
index 7bcad4c..438eac5 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java
@@ -29,10 +29,11 @@ public class GoogleJavaFormatToolProvider implements ToolProvider {
@Override
public int run(PrintWriter out, PrintWriter err, String... args) {
try {
- return Main.main(out, err, args);
+ return Main.main(System.in, out, err, args);
} catch (RuntimeException e) {
err.print(e.getMessage());
- return -1; // pass non-zero value back indicating an error has happened
+ err.flush();
+ return 1; // pass non-zero value back indicating an error has happened
}
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java b/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
index 346324a..d34ecc4 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaCommentsHelper.java
@@ -53,11 +53,13 @@ public final class JavaCommentsHelper implements CommentsHelper {
}
if (tok.isSlashSlashComment()) {
return indentLineComments(lines, column0);
- } else if (javadocShaped(lines)) {
- return indentJavadoc(lines, column0);
- } else {
- return preserveIndentation(lines, column0);
}
+ return CommentsHelper.reformatParameterComment(tok)
+ .orElseGet(
+ () ->
+ javadocShaped(lines)
+ ? indentJavadoc(lines, column0)
+ : preserveIndentation(lines, column0));
}
// For non-javadoc-shaped block comments, shift the entire block to the correct
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
index fbb6fe7..67c13d0 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java
@@ -14,6 +14,7 @@
package com.google.googlejavaformat.java;
+import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.Immutable;
/**
@@ -27,7 +28,8 @@ import com.google.errorprone.annotations.Immutable;
* preferences, and in fact it would work directly against our primary goals.
*/
@Immutable
-public class JavaFormatterOptions {
+@AutoValue
+public abstract class JavaFormatterOptions {
public enum Style {
/** The default Google Java Style configuration. */
@@ -47,27 +49,17 @@ public class JavaFormatterOptions {
}
}
- private final Style style;
- private final boolean formatJavadoc;
-
- private JavaFormatterOptions(Style style, boolean formatJavadoc) {
- this.style = style;
- this.formatJavadoc = formatJavadoc;
- }
-
/** Returns the multiplier for the unit of indent. */
public int indentationMultiplier() {
- return style.indentationMultiplier();
+ return style().indentationMultiplier();
}
- public boolean formatJavadoc() {
- return formatJavadoc;
- }
+ public abstract boolean formatJavadoc();
+
+ public abstract boolean reorderModifiers();
/** Returns the code style. */
- public Style style() {
- return style;
- }
+ public abstract Style style();
/** Returns the default formatting options. */
public static JavaFormatterOptions defaultOptions() {
@@ -76,28 +68,22 @@ public class JavaFormatterOptions {
/** Returns a builder for {@link JavaFormatterOptions}. */
public static Builder builder() {
- return new Builder();
+ return new AutoValue_JavaFormatterOptions.Builder()
+ .style(Style.GOOGLE)
+ .formatJavadoc(true)
+ .reorderModifiers(true);
}
/** A builder for {@link JavaFormatterOptions}. */
- public static class Builder {
- private Style style = Style.GOOGLE;
- private boolean formatJavadoc = true;
+ @AutoValue.Builder
+ public abstract static class Builder {
- private Builder() {}
+ public abstract Builder style(Style style);
- public Builder style(Style style) {
- this.style = style;
- return this;
- }
+ public abstract Builder formatJavadoc(boolean formatJavadoc);
- public Builder formatJavadoc(boolean formatJavadoc) {
- this.formatJavadoc = formatJavadoc;
- return this;
- }
+ public abstract Builder reorderModifiers(boolean reorderModifiers);
- public JavaFormatterOptions build() {
- return new JavaFormatterOptions(style, formatJavadoc);
- }
+ public abstract JavaFormatterOptions build();
}
}
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 165bdeb..7b5eb84 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java
@@ -49,6 +49,7 @@ import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
@@ -349,7 +350,8 @@ public final class JavaInput extends Input {
stopTokens = ImmutableSet.<TokenKind>builder().addAll(stopTokens).add(TokenKind.EOF).build();
Context context = new Context();
Options.instance(context).put("--enable-preview", "true");
- new JavacFileManager(context, true, UTF_8);
+ JavaFileManager fileManager = new JavacFileManager(context, false, UTF_8);
+ context.put(JavaFileManager.class, fileManager);
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
context.put(DiagnosticListener.class, diagnosticCollector);
Log log = Log.instance(context);
@@ -385,7 +387,14 @@ public final class JavaInput extends Input {
final boolean isNumbered; // Is this tok numbered? (tokens and comments)
String extraNewline = null; // Extra newline at end?
List<String> strings = new ArrayList<>();
- if (Character.isWhitespace(tokText0)) {
+ if (tokText.startsWith("'")
+ || tokText.startsWith("\"")
+ || JavacTokens.isStringFragment(t.kind())) {
+ // Perform this check first, STRINGFRAGMENT tokens can start with arbitrary characters.
+ isToken = true;
+ isNumbered = true;
+ strings.add(originalTokText);
+ } else if (Character.isWhitespace(tokText0)) {
isToken = false;
isNumbered = false;
Iterator<String> it = Newlines.lineIterator(originalTokText);
@@ -402,10 +411,6 @@ public final class JavaInput extends Input {
strings.add(line);
}
}
- } else if (tokText.startsWith("'") || tokText.startsWith("\"")) {
- isToken = true;
- isNumbered = true;
- strings.add(originalTokText);
} else if (tokText.startsWith("//") || tokText.startsWith("/*")) {
// For compatibility with an earlier lexer, the newline after a // comment is its own tok.
if (tokText.startsWith("//")
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 daed250..ea967b3 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java
@@ -285,7 +285,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
ImmutableSetMultimap.Builder<String, String> result = ImmutableSetMultimap.builder();
for (String annotation :
ImmutableList.of(
+ "org.jspecify.annotations.NonNull",
+ "org.jspecify.annotations.Nullable",
"org.jspecify.nullness.Nullable",
+ "org.checkerframework.checker.nullness.qual.NonNull",
"org.checkerframework.checker.nullness.qual.Nullable")) {
String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1);
result.put(simpleName, annotation);
@@ -927,7 +930,6 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
@Override
public Void visitMemberReference(MemberReferenceTree node, Void unused) {
- sync(node);
builder.open(plusFour);
scan(node.getQualifierExpression(), null);
builder.breakOp();
@@ -1247,7 +1249,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
token(",");
builder.breakOp(" ");
}
- scan(parameter, null);
+ visitVariables(
+ ImmutableList.of(parameter),
+ DeclarationKind.NONE,
+ fieldAnnotationDirection(parameter.getModifiers()));
first = false;
}
if (parens) {
@@ -2639,7 +2644,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
for (ExpressionTree thrownExceptionType : thrownExceptionTypes) {
if (!first) {
token(",");
- builder.breakToFill(" ");
+ builder.breakOp(" ");
}
scan(thrownExceptionType, null);
first = false;
@@ -3558,7 +3563,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
if (receiverExpression.isPresent()) {
scan(receiverExpression.get(), null);
} else {
- visit(name);
+ variableName(name);
}
builder.op(op);
}
@@ -3601,6 +3606,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
return baseDims;
}
+ protected void variableName(Name name) {
+ visit(name);
+ }
+
private void maybeAddDims(Deque<List<? extends AnnotationTree>> annotations) {
maybeAddDims(new ArrayDeque<>(), annotations);
}
@@ -3691,7 +3700,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
builder.breakOp(" ");
builder.open(ZERO);
maybeAddDims(dims);
- visit(fragment.getName());
+ variableName(fragment.getName());
maybeAddDims(dims);
ExpressionTree initializer = fragment.getInitializer();
if (initializer != null) {
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java
index c43a91a..656b65c 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java
@@ -111,7 +111,7 @@ public final class JavaOutput extends Output {
* there's a blank line here and it's a comment.
*/
BlankLineWanted wanted = blankLines.getOrDefault(lastK, BlankLineWanted.NO);
- if (isComment(text) ? sawNewlines : wanted.wanted().orElse(sawNewlines)) {
+ if ((sawNewlines && isComment(text)) || wanted.wanted().orElse(sawNewlines)) {
++newlinesPending;
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
index ba7e3b7..dd8760b 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java
@@ -15,6 +15,7 @@
package com.google.googlejavaformat.java;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Arrays.stream;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -27,6 +28,7 @@ import com.sun.tools.javac.parser.Tokens.Token;
import com.sun.tools.javac.parser.Tokens.TokenKind;
import com.sun.tools.javac.parser.UnicodeReader;
import com.sun.tools.javac.util.Context;
+import java.util.Objects;
import java.util.Set;
/** A wrapper around javac's lexer. */
@@ -71,6 +73,16 @@ class JavacTokens {
}
}
+ private static final TokenKind STRINGFRAGMENT =
+ stream(TokenKind.values())
+ .filter(t -> t.name().contentEquals("STRINGFRAGMENT"))
+ .findFirst()
+ .orElse(null);
+
+ static boolean isStringFragment(TokenKind kind) {
+ return STRINGFRAGMENT != null && Objects.equals(kind, STRINGFRAGMENT);
+ }
+
/** Lex the input and return a list of {@link RawTok}s. */
public static ImmutableList<RawTok> getTokens(
String source, Context context, Set<TokenKind> stopTokens) {
@@ -106,13 +118,39 @@ class JavacTokens {
if (last < t.pos) {
tokens.add(new RawTok(null, null, last, t.pos));
}
- tokens.add(
- new RawTok(
- t.kind == TokenKind.STRINGLITERAL ? "\"" + t.stringVal() + "\"" : null,
- t.kind,
- t.pos,
- t.endPos));
- last = t.endPos;
+ int pos = t.pos;
+ int endPos = t.endPos;
+ if (isStringFragment(t.kind)) {
+ // A string template is tokenized as a series of STRINGFRAGMENT tokens containing the string
+ // literal values, followed by the tokens for the template arguments. For the formatter, we
+ // want the stream of tokens to appear in order by their start position, and also to have
+ // all the content from the original source text (including leading and trailing ", and the
+ // \ escapes from template arguments). This logic processes the token stream from javac to
+ // meet those requirements.
+ while (isStringFragment(t.kind)) {
+ endPos = t.endPos;
+ scanner.nextToken();
+ t = scanner.token();
+ }
+ // Read tokens for the string template arguments, until we read the end of the string
+ // template. The last token in a string template is always a trailing string fragment. Use
+ // lookahead to defer reading the token after the template until the next iteration of the
+ // outer loop.
+ while (scanner.token(/* lookahead= */ 1).endPos < endPos) {
+ scanner.nextToken();
+ t = scanner.token();
+ }
+ tokens.add(new RawTok(source.substring(pos, endPos), t.kind, pos, endPos));
+ last = endPos;
+ } else {
+ tokens.add(
+ new RawTok(
+ t.kind == TokenKind.STRINGLITERAL ? "\"" + t.stringVal() + "\"" : null,
+ t.kind,
+ t.pos,
+ t.endPos));
+ last = t.endPos;
+ }
} while (scanner.token().kind != TokenKind.EOF);
if (last < end) {
tokens.add(new RawTok(null, null, last, end));
diff --git a/core/src/main/java/com/google/googlejavaformat/java/Main.java b/core/src/main/java/com/google/googlejavaformat/java/Main.java
index 953ca58..0845e0e 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/Main.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java
@@ -16,25 +16,30 @@ package com.google.googlejavaformat.java;
import static java.lang.Math.min;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Comparator.comparing;
import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.MoreExecutors;
import com.google.googlejavaformat.FormatterDiagnostic;
import com.google.googlejavaformat.java.JavaFormatterOptions.Style;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
+import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
/** The main class for the Java formatter CLI. */
public final class Main {
@@ -62,24 +67,33 @@ public final class Main {
*
* @param args the command-line arguments
*/
- public static void main(String[] args) {
- PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out, UTF_8));
- PrintWriter err = new PrintWriter(new OutputStreamWriter(System.err, UTF_8));
- int result = main(out, err, args);
+ public static void main(String... args) {
+ int result = main(System.in, System.out, System.err, args);
System.exit(result);
}
/**
- * Package-private main entry point used this CLI program and the java.util.spi.ToolProvider
+ * Package-private main entry point used by the {@link javax.tools.Tool Tool} implementation in
+ * the same package as this Main class.
+ */
+ static int main(InputStream in, PrintStream out, PrintStream err, String... args) {
+ PrintWriter outWriter = new PrintWriter(new OutputStreamWriter(out, UTF_8));
+ PrintWriter errWriter = new PrintWriter(new OutputStreamWriter(err, UTF_8));
+ return main(in, outWriter, errWriter, args);
+ }
+
+ /**
+ * Package-private main entry point used by the {@link java.util.spi.ToolProvider ToolProvider}
* implementation in the same package as this Main class.
*/
- static int main(PrintWriter out, PrintWriter err, String... args) {
+ static int main(InputStream in, PrintWriter out, PrintWriter err, String... args) {
try {
- Main formatter = new Main(out, err, System.in);
+ Main formatter = new Main(out, err, in);
return formatter.format(args);
} catch (UsageException e) {
err.print(e.getMessage());
- return 0;
+ // We return exit code 2 to differentiate usage issues from code formatting issues.
+ return 2;
} finally {
err.flush();
out.flush();
@@ -120,50 +134,55 @@ public final class Main {
int numThreads = min(MAX_THREADS, parameters.files().size());
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
- Map<Path, String> inputs = new LinkedHashMap<>();
- Map<Path, Future<String>> results = new LinkedHashMap<>();
+ ExecutorCompletionService<FormatFileCallable.Result> cs =
+ new ExecutorCompletionService<>(executorService);
boolean allOk = true;
+ int files = 0;
for (String fileName : parameters.files()) {
if (!fileName.endsWith(".java")) {
errWriter.println("Skipping non-Java file: " + fileName);
continue;
}
Path path = Paths.get(fileName);
- String input;
try {
- input = new String(Files.readAllBytes(path), UTF_8);
- inputs.put(path, input);
- results.put(
- path, executorService.submit(new FormatFileCallable(parameters, input, options)));
+ String input = new String(Files.readAllBytes(path), UTF_8);
+ cs.submit(new FormatFileCallable(parameters, path, input, options));
+ files++;
} catch (IOException e) {
errWriter.println(fileName + ": could not read file: " + e.getMessage());
allOk = false;
}
}
- for (Map.Entry<Path, Future<String>> result : results.entrySet()) {
- Path path = result.getKey();
- String formatted;
+ List<FormatFileCallable.Result> results = new ArrayList<>();
+ while (files > 0) {
try {
- formatted = result.getValue().get();
+ files--;
+ results.add(cs.take().get());
} catch (InterruptedException e) {
errWriter.println(e.getMessage());
allOk = false;
continue;
} catch (ExecutionException e) {
- if (e.getCause() instanceof FormatterException) {
- for (FormatterDiagnostic diagnostic : ((FormatterException) e.getCause()).diagnostics()) {
- errWriter.println(path + ":" + diagnostic);
- }
- } else {
- errWriter.println(path + ": error: " + e.getCause().getMessage());
- e.getCause().printStackTrace(errWriter);
+ errWriter.println("error: " + e.getCause().getMessage());
+ e.getCause().printStackTrace(errWriter);
+ allOk = false;
+ continue;
+ }
+ }
+ Collections.sort(results, comparing(FormatFileCallable.Result::path));
+ for (FormatFileCallable.Result result : results) {
+ Path path = result.path();
+ if (result.exception() != null) {
+ for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) {
+ errWriter.println(path + ":" + diagnostic);
}
allOk = false;
continue;
}
- boolean changed = !formatted.equals(inputs.get(path));
+ String formatted = result.output();
+ boolean changed = result.changed();
if (changed && parameters.setExitIfChanged()) {
allOk = false;
}
@@ -186,6 +205,10 @@ public final class Main {
outWriter.write(formatted);
}
}
+ if (!MoreExecutors.shutdownAndAwaitTermination(executorService, Duration.ofSeconds(5))) {
+ errWriter.println("Failed to shut down ExecutorService");
+ allOk = false;
+ }
return allOk ? 0 : 1;
}
@@ -198,9 +221,16 @@ public final class Main {
}
String stdinFilename = parameters.assumeFilename().orElse(STDIN_FILENAME);
boolean ok = true;
- try {
- String output = new FormatFileCallable(parameters, input, options).call();
- boolean changed = !input.equals(output);
+ FormatFileCallable.Result result =
+ new FormatFileCallable(parameters, null, input, options).call();
+ if (result.exception() != null) {
+ for (FormatterDiagnostic diagnostic : result.exception().diagnostics()) {
+ errWriter.println(stdinFilename + ":" + diagnostic);
+ }
+ ok = false;
+ } else {
+ String output = result.output();
+ boolean changed = result.changed();
if (changed && parameters.setExitIfChanged()) {
ok = false;
}
@@ -211,12 +241,6 @@ public final class Main {
} else {
outWriter.write(output);
}
- } catch (FormatterException e) {
- for (FormatterDiagnostic diagnostic : e.diagnostics()) {
- errWriter.println(stdinFilename + ":" + diagnostic);
- }
- ok = false;
- // TODO(cpovirk): Catch other types of exception (as we do in the formatFiles case).
}
return ok ? 0 : 1;
}
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 20e55e9..a0fc2f5 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java
@@ -49,7 +49,6 @@ import com.sun.tools.javac.tree.DCTree.DCReference;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
-import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
@@ -252,7 +251,10 @@ public class RemoveUnusedImports {
ParserFactory parserFactory = ParserFactory.instance(context);
JavacParser parser =
parserFactory.newParser(
- javaInput, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
+ javaInput,
+ /* keepDocComments= */ true,
+ /* keepEndPos= */ true,
+ /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = source;
Iterable<Diagnostic<? extends JavaFileObject>> errorDiagnostics =
@@ -290,9 +292,7 @@ public class RemoveUnusedImports {
}
private static String getSimpleName(JCImport importTree) {
- return importTree.getQualifiedIdentifier() instanceof JCIdent
- ? ((JCIdent) importTree.getQualifiedIdentifier()).getName().toString()
- : ((JCFieldAccess) importTree.getQualifiedIdentifier()).getIdentifier().toString();
+ return getQualifiedIdentifier(importTree).getIdentifier().toString();
}
private static boolean isUnused(
@@ -301,18 +301,15 @@ public class RemoveUnusedImports {
Multimap<String, Range<Integer>> usedInJavadoc,
JCImport importTree,
String simpleName) {
- String qualifier =
- ((JCFieldAccess) importTree.getQualifiedIdentifier()).getExpression().toString();
+ JCFieldAccess qualifiedIdentifier = getQualifiedIdentifier(importTree);
+ String qualifier = qualifiedIdentifier.getExpression().toString();
if (qualifier.equals("java.lang")) {
return true;
}
if (unit.getPackageName() != null && unit.getPackageName().toString().equals(qualifier)) {
return true;
}
- if (importTree.getQualifiedIdentifier() instanceof JCFieldAccess
- && ((JCFieldAccess) importTree.getQualifiedIdentifier())
- .getIdentifier()
- .contentEquals("*")) {
+ if (qualifiedIdentifier.getIdentifier().contentEquals("*")) {
return false;
}
@@ -325,6 +322,15 @@ public class RemoveUnusedImports {
return true;
}
+ private static JCFieldAccess getQualifiedIdentifier(JCImport importTree) {
+ // Use reflection because the return type is JCTree in some versions and JCFieldAccess in others
+ try {
+ return (JCFieldAccess) JCImport.class.getMethod("getQualifiedIdentifier").invoke(importTree);
+ } catch (ReflectiveOperationException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ }
+
/** Applies the replacements to the given source, and re-format any edited javadoc. */
private static String applyReplacements(String source, RangeMap<Integer, String> replacements) {
// save non-empty fixed ranges for reformatting after fixes are applied
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 c0f16e9..f241ae4 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java
@@ -15,6 +15,7 @@
package com.google.googlejavaformat.java;
import static com.google.common.collect.Iterables.getLast;
+import static java.lang.Math.min;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
@@ -96,7 +97,9 @@ public final class StringWrapper {
if (!expected.equals(actual)) {
throw new FormatterException(
String.format(
- "Something has gone terribly wrong. Please file a bug: "
+ "Something has gone terribly wrong. We planned to make the below formatting change,"
+ + " but have aborted because it would unexpectedly change the AST.\n"
+ + "Please file a bug: "
+ "https://github.com/google/google-java-format/issues/new"
+ "\n\n=== Actual: ===\n%s\n=== Expected: ===\n%s\n",
actual, expected));
@@ -120,6 +123,10 @@ public final class StringWrapper {
if (literalTree.getKind() != Kind.STRING_LITERAL) {
return null;
}
+ int pos = getStartPosition(literalTree);
+ if (input.substring(pos, min(input.length(), pos + 3)).equals("\"\"\"")) {
+ return null;
+ }
Tree parent = getCurrentPath().getParentPath().getLeaf();
if (parent instanceof MemberSelectTree
&& ((MemberSelectTree) parent).getExpression().equals(literalTree)) {
@@ -396,7 +403,7 @@ public final class StringWrapper {
ParserFactory parserFactory = ParserFactory.instance(context);
JavacParser parser =
parserFactory.newParser(
- source, /*keepDocComments=*/ true, /*keepEndPos=*/ true, /*keepLineMap=*/ true);
+ source, /* keepDocComments= */ true, /* keepEndPos= */ true, /* keepLineMap= */ true);
unit = parser.parseCompilationUnit();
unit.sourcefile = sjfo;
Iterable<Diagnostic<? extends JavaFileObject>> errorDiagnostics =
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
index 890687f..6818f4a 100644
--- a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java
+++ b/core/src/main/java/com/google/googlejavaformat/java/java17/Java17InputAstVisitor.java
@@ -12,7 +12,7 @@
* the License.
*/
-package com.google.googlejavaformat.java.java14;
+package com.google.googlejavaformat.java.java17;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getOnlyElement;
@@ -25,9 +25,11 @@ import com.google.googlejavaformat.java.JavaInputAstVisitor;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.BindingPatternTree;
import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseLabelTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.ModuleTree;
@@ -39,39 +41,23 @@ import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeInfo;
-import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.Name;
/**
- * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for
- * Java 14.
+ * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified in
+ * Java 17.
*/
-public class Java14InputAstVisitor extends JavaInputAstVisitor {
- private static final Method COMPILATION_UNIT_TREE_GET_MODULE =
- maybeGetMethod(CompilationUnitTree.class, "getModule");
- private static final Method CLASS_TREE_GET_PERMITS_CLAUSE =
- maybeGetMethod(ClassTree.class, "getPermitsClause");
- private static final Method BINDING_PATTERN_TREE_GET_VARIABLE =
- maybeGetMethod(BindingPatternTree.class, "getVariable");
- private static final Method BINDING_PATTERN_TREE_GET_TYPE =
- maybeGetMethod(BindingPatternTree.class, "getType");
- private static final Method BINDING_PATTERN_TREE_GET_BINDING =
- maybeGetMethod(BindingPatternTree.class, "getBinding");
- private static final Method CASE_TREE_GET_LABELS = maybeGetMethod(CaseTree.class, "getLabels");
+public class Java17InputAstVisitor extends JavaInputAstVisitor {
- public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
+ public Java17InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
super(builder, indentMultiplier);
}
@Override
protected void handleModule(boolean first, CompilationUnitTree node) {
- if (COMPILATION_UNIT_TREE_GET_MODULE == null) {
- // Java < 17, see https://bugs.openjdk.java.net/browse/JDK-8255464
- return;
- }
- ModuleTree module = (ModuleTree) invoke(COMPILATION_UNIT_TREE_GET_MODULE, node);
+ ModuleTree module = node.getModule();
if (module != null) {
if (!first) {
builder.blankLineWanted(BlankLineWanted.YES);
@@ -84,34 +70,20 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
@Override
protected List<? extends Tree> getPermitsClause(ClassTree node) {
- if (CLASS_TREE_GET_PERMITS_CLAUSE != null) {
- return (List<? extends Tree>) invoke(CLASS_TREE_GET_PERMITS_CLAUSE, node);
- } else {
- // Java < 15
- return super.getPermitsClause(node);
- }
+ return node.getPermitsClause();
}
@Override
public Void visitBindingPattern(BindingPatternTree node, Void unused) {
sync(node);
- if (BINDING_PATTERN_TREE_GET_VARIABLE != null) {
- VariableTree variableTree = (VariableTree) invoke(BINDING_PATTERN_TREE_GET_VARIABLE, node);
- visitBindingPattern(
- variableTree.getModifiers(), variableTree.getType(), variableTree.getName());
- } else if (BINDING_PATTERN_TREE_GET_TYPE != null && BINDING_PATTERN_TREE_GET_BINDING != null) {
- Tree type = (Tree) invoke(BINDING_PATTERN_TREE_GET_TYPE, node);
- Name name = (Name) invoke(BINDING_PATTERN_TREE_GET_BINDING, node);
- visitBindingPattern(/* modifiers= */ null, type, name);
- } else {
- throw new LinkageError(
- "BindingPatternTree must have either getVariable() or both getType() and getBinding(),"
- + " but does not");
- }
+ VariableTree variableTree = node.getVariable();
+ visitBindingPattern(
+ variableTree.getModifiers(), variableTree.getType(), variableTree.getName());
return null;
}
private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) {
+ builder.open(plusFour);
if (modifiers != null) {
List<AnnotationTree> annotations =
visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty());
@@ -119,7 +91,12 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
}
scan(type, null);
builder.breakOp(" ");
- visit(name);
+ if (name.isEmpty()) {
+ token("_");
+ } else {
+ visit(name);
+ }
+ builder.close();
}
@Override
@@ -248,17 +225,14 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
sync(node);
markForPartialFormat();
builder.forcedBreak();
- List<? extends Tree> labels;
- boolean isDefault;
- if (CASE_TREE_GET_LABELS != null) {
- labels = (List<? extends Tree>) invoke(CASE_TREE_GET_LABELS, node);
- isDefault =
- labels.size() == 1
- && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL");
- } else {
- labels = node.getExpressions();
- isDefault = labels.isEmpty();
- }
+ List<? extends CaseLabelTree> labels = node.getLabels();
+ boolean isDefault =
+ labels.size() == 1 && getOnlyElement(labels).getKind().name().equals("DEFAULT_CASE_LABEL");
+ builder.open(
+ node.getCaseKind().equals(CaseTree.CaseKind.RULE)
+ && !node.getBody().getKind().equals(Tree.Kind.BLOCK)
+ ? plusFour
+ : ZERO);
if (isDefault) {
token("default", plusTwo);
} else {
@@ -276,6 +250,15 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
}
builder.close();
}
+
+ final ExpressionTree guard = getGuard(node);
+ if (guard != null) {
+ builder.space();
+ token("when");
+ builder.space();
+ scan(guard, null);
+ }
+
switch (node.getCaseKind()) {
case STATEMENT:
token(":");
@@ -287,8 +270,8 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
builder.space();
token("-");
token(">");
- builder.space();
if (node.getBody().getKind() == Tree.Kind.BLOCK) {
+ builder.space();
// Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks.
visitBlock(
(BlockTree) node.getBody(),
@@ -296,6 +279,7 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
AllowLeadingBlankLine.NO,
AllowTrailingBlankLine.NO);
} else {
+ builder.breakOp(" ");
scan(node.getBody(), null);
}
builder.guessToken(";");
@@ -303,22 +287,11 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor {
default:
throw new AssertionError(node.getCaseKind());
}
+ builder.close();
return null;
}
- private static Method maybeGetMethod(Class<?> c, String name) {
- try {
- return c.getMethod(name);
- } catch (ReflectiveOperationException e) {
- return null;
- }
- }
-
- private static Object invoke(Method m, Object target) {
- try {
- return m.invoke(target);
- } catch (ReflectiveOperationException e) {
- throw new LinkageError(e.getMessage(), e);
- }
+ protected ExpressionTree getGuard(final CaseTree node) {
+ return null;
}
}
diff --git a/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
new file mode 100644
index 0000000..897d6ff
--- /dev/null
+++ b/core/src/main/java/com/google/googlejavaformat/java/java21/Java21InputAstVisitor.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 The google-java-format Authors.
+ *
+ * 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.java21;
+
+import com.google.googlejavaformat.OpsBuilder;
+import com.google.googlejavaformat.java.java17.Java17InputAstVisitor;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.ConstantCaseLabelTree;
+import com.sun.source.tree.DeconstructionPatternTree;
+import com.sun.source.tree.DefaultCaseLabelTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.PatternCaseLabelTree;
+import com.sun.source.tree.PatternTree;
+import com.sun.source.tree.StringTemplateTree;
+import javax.lang.model.element.Name;
+
+/**
+ * Extends {@link Java17InputAstVisitor} with support for AST nodes that were added or modified in
+ * Java 21.
+ */
+public class Java21InputAstVisitor extends Java17InputAstVisitor {
+
+ public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) {
+ super(builder, indentMultiplier);
+ }
+
+ @Override
+ protected ExpressionTree getGuard(final CaseTree node) {
+ return node.getGuard();
+ }
+
+ @Override
+ public Void visitDefaultCaseLabel(DefaultCaseLabelTree node, Void unused) {
+ token("default");
+ return null;
+ }
+
+ @Override
+ public Void visitPatternCaseLabel(PatternCaseLabelTree node, Void unused) {
+ scan(node.getPattern(), null);
+ return null;
+ }
+
+ @Override
+ public Void visitConstantCaseLabel(ConstantCaseLabelTree node, Void aVoid) {
+ scan(node.getConstantExpression(), null);
+ return null;
+ }
+
+ @Override
+ public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void unused) {
+ sync(node);
+ scan(node.getDeconstructor(), null);
+ builder.open(plusFour);
+ token("(");
+ builder.breakOp();
+ boolean first = true;
+ for (PatternTree pattern : node.getNestedPatterns()) {
+ if (!first) {
+ token(",");
+ builder.breakOp(" ");
+ }
+ first = false;
+ scan(pattern, null);
+ }
+ builder.close();
+ token(")");
+ return null;
+ }
+
+ @SuppressWarnings("preview")
+ @Override
+ public Void visitStringTemplate(StringTemplateTree node, Void aVoid) {
+ sync(node);
+ scan(node.getProcessor(), null);
+ token(".");
+ token(builder.peekToken().get());
+ return null;
+ }
+
+ @Override
+ protected void variableName(Name name) {
+ if (name.isEmpty()) {
+ token("_");
+ } else {
+ visit(name);
+ }
+ }
+}
diff --git a/core/src/main/resources/META-INF/native-image/reflect-config.json b/core/src/main/resources/META-INF/native-image/reflect-config.json
index 2c65803..4d30840 100644
--- a/core/src/main/resources/META-INF/native-image/reflect-config.json
+++ b/core/src/main/resources/META-INF/native-image/reflect-config.json
@@ -2,5 +2,23 @@
{
"name": "com.sun.tools.javac.parser.UnicodeReader",
"allDeclaredMethods": true
+ },
+ {
+ "name": "com.google.googlejavaformat.java.java17.Java17InputAstVisitor",
+ "methods": [
+ {
+ "name": "<init>",
+ "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"]
+ }
+ ]
+ },
+ {
+ "name": "com.google.googlejavaformat.java.java21.Java21InputAstVisitor",
+ "methods": [
+ {
+ "name": "<init>",
+ "parameterTypes": ["com.google.googlejavaformat.OpsBuilder", "int"]
+ }
+ ]
}
]