diff options
Diffstat (limited to 'core/src')
92 files changed, 1443 insertions, 388 deletions
diff --git a/core/src/main/java/com/google/googlejavaformat/Doc.java b/core/src/main/java/com/google/googlejavaformat/Doc.java index e663c96..35acca3 100644 --- a/core/src/main/java/com/google/googlejavaformat/Doc.java +++ b/core/src/main/java/com/google/googlejavaformat/Doc.java @@ -15,6 +15,7 @@ package com.google.googlejavaformat; import static com.google.common.collect.Iterables.getLast; +import static java.lang.Math.max; import com.google.common.base.MoreObjects; import com.google.common.collect.DiscreteDomain; @@ -653,7 +654,7 @@ public abstract class Doc { if (broken) { this.broken = true; - this.newIndent = Math.max(lastIndent + plusIndent.eval(), 0); + this.newIndent = max(lastIndent + plusIndent.eval(), 0); return state.withColumn(newIndent); } else { this.broken = false; diff --git a/core/src/main/java/com/google/googlejavaformat/Input.java b/core/src/main/java/com/google/googlejavaformat/Input.java index 9e17c2b..66a3921 100644 --- a/core/src/main/java/com/google/googlejavaformat/Input.java +++ b/core/src/main/java/com/google/googlejavaformat/Input.java @@ -63,7 +63,7 @@ public abstract class Input extends InputOutput { /** Is the {@code Tok} a "//" comment? */ boolean isSlashSlashComment(); - /** Is the {@code Tok} a "//" comment? */ + /** Is the {@code Tok} a "/*" comment? */ boolean isSlashStarComment(); /** Is the {@code Tok} a javadoc comment? */ @@ -114,14 +114,14 @@ public abstract class Input extends InputOutput { /** * Get the number of toks. * - * @return the number of toks, including the EOF tok + * @return the number of toks, excluding the EOF tok */ public abstract int getkN(); /** * Get the Token by index. * - * @param k the token index + * @param k the Tok index */ public abstract Token getToken(int k); diff --git a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java index e8e100f..db431c0 100644 --- a/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java +++ b/core/src/main/java/com/google/googlejavaformat/OpsBuilder.java @@ -14,7 +14,12 @@ package com.google.googlejavaformat; +import static java.lang.Math.max; +import static java.lang.Math.min; + import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -39,14 +44,14 @@ public final class OpsBuilder { int start = startToken.getTok().getPosition(); for (Tok tok : startToken.getToksBefore()) { if (tok.isComment()) { - start = Math.min(start, tok.getPosition()); + start = min(start, tok.getPosition()); } } Token endToken = input.getPositionTokenMap().get(position + length - 1); int end = endToken.getTok().getPosition() + endToken.getTok().length(); for (Tok tok : endToken.getToksAfter()) { if (tok.isComment()) { - end = Math.max(end, tok.getPosition() + tok.length()); + end = max(end, tok.getPosition() + tok.length()); } } return end - start; @@ -62,7 +67,7 @@ public final class OpsBuilder { return start; } if (tok.isComment()) { - start = Math.min(start, tok.getPosition()); + start = min(start, tok.getPosition()); } } return start; @@ -279,6 +284,28 @@ public final class OpsBuilder { } /** + * Returns the {@link Input.Tok}s starting at the current source position, which are satisfied by + * the given predicate. + */ + public ImmutableList<Tok> peekTokens(int startPosition, Predicate<Input.Tok> predicate) { + ImmutableList<? extends Input.Token> tokens = input.getTokens(); + Preconditions.checkState( + tokens.get(tokenI).getTok().getPosition() == startPosition, + "Expected the current token to be at position %s, found: %s", + startPosition, + tokens.get(tokenI)); + ImmutableList.Builder<Tok> result = ImmutableList.builder(); + for (int idx = tokenI; idx < tokens.size(); idx++) { + Tok tok = tokens.get(idx).getTok(); + if (!predicate.apply(tok)) { + break; + } + result.add(tok); + } + return result.build(); + } + + /** * Emit an optional token iff it exists on the input. This is used to emit tokens whose existence * has been lost in the AST. * diff --git a/core/src/main/java/com/google/googlejavaformat/java/CommandLineOptionsParser.java b/core/src/main/java/com/google/googlejavaformat/java/CommandLineOptionsParser.java index 2023826..f7c3dec 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/CommandLineOptionsParser.java +++ b/core/src/main/java/com/google/googlejavaformat/java/CommandLineOptionsParser.java @@ -54,7 +54,7 @@ final class CommandLineOptionsParser { int idx = option.indexOf('='); if (idx >= 0) { flag = option.substring(0, idx); - value = option.substring(idx + 1, option.length()); + value = option.substring(idx + 1); } else { flag = option; value = null; 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 3e97395..aac829d 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -14,8 +14,6 @@ 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; @@ -42,7 +40,6 @@ import com.sun.tools.javac.util.Log; 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; @@ -154,7 +151,7 @@ public final class Formatter { OpsBuilder builder = new OpsBuilder(javaInput, javaOutput); // Output the compilation unit. JavaInputAstVisitor visitor; - if (getMajor() >= 14) { + if (Runtime.version().feature() >= 14) { try { visitor = Class.forName("com.google.googlejavaformat.java.java14.Java14InputAstVisitor") @@ -176,23 +173,6 @@ public final class Formatter { 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/FormatterException.java b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java index 3ccb44a..808916c 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java +++ b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java @@ -26,7 +26,7 @@ import javax.tools.JavaFileObject; /** Checked exception class for formatter errors. */ public final class FormatterException extends Exception { - private ImmutableList<FormatterDiagnostic> diagnostics; + private final ImmutableList<FormatterDiagnostic> diagnostics; public FormatterException(String message) { this(FormatterDiagnostic.create(message)); @@ -47,7 +47,8 @@ public final class FormatterException extends Exception { public static FormatterException fromJavacDiagnostics( Iterable<Diagnostic<? extends JavaFileObject>> diagnostics) { - return new FormatterException(Iterables.transform(diagnostics, d -> toFormatterDiagnostic(d))); + return new FormatterException( + Iterables.transform(diagnostics, FormatterException::toFormatterDiagnostic)); } private static FormatterDiagnostic toFormatterDiagnostic(Diagnostic<?> input) { diff --git a/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java new file mode 100644 index 0000000..7bcad4c --- /dev/null +++ b/core/src/main/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProvider.java @@ -0,0 +1,38 @@ +/* + * 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 com.google.auto.service.AutoService; +import java.io.PrintWriter; +import java.util.spi.ToolProvider; + +/** Provide a way to be invoked without necessarily starting a new VM. */ +@AutoService(ToolProvider.class) +public class GoogleJavaFormatToolProvider implements ToolProvider { + @Override + public String name() { + return "google-java-format"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + try { + return Main.main(out, err, args); + } catch (RuntimeException e) { + err.print(e.getMessage()); + return -1; // pass non-zero value back indicating an error has happened + } + } +} diff --git a/core/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java b/core/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java index a82715e..dcbaea1 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java +++ b/core/src/main/java/com/google/googlejavaformat/java/ImportOrderer.java @@ -346,6 +346,10 @@ public class ImportOrderer { i++; } } + while (tokenAt(i).equals(";")) { + // Extra semicolons are not allowed by the JLS but are accepted by javac. + i++; + } imports.add(new Import(importedName, trailing.toString(), isStatic)); // Remember the position just after the import we just saw, before skipping blank lines. // If the next thing after the blank lines is not another import then we don't want to 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 4d3d30d..fbb6fe7 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaFormatterOptions.java @@ -60,7 +60,7 @@ public class JavaFormatterOptions { return style.indentationMultiplier(); } - boolean formatJavadoc() { + public boolean formatJavadoc() { return formatJavadoc; } @@ -91,7 +91,7 @@ public class JavaFormatterOptions { return this; } - Builder formatJavadoc(boolean formatJavadoc) { + public Builder formatJavadoc(boolean formatJavadoc) { this.formatJavadoc = formatJavadoc; return this; } 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 999c8fb..165bdeb 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInput.java @@ -311,7 +311,7 @@ public final class JavaInput extends Input { for (Tok tok : toks) { builder.put(tok.getPosition(), tok.getColumn()); } - return builder.build(); + return builder.buildOrThrow(); } /** @@ -557,33 +557,28 @@ public final class JavaInput extends Input { } /** - * Convert from an offset and length flag pair to a token range. + * Convert from a character range to a token range. * - * @param offset the {@code 0}-based offset in characters - * @param length the length in characters + * @param characterRange the {@code 0}-based {@link Range} of characters * @return the {@code 0}-based {@link Range} of tokens - * @throws FormatterException if offset + length is outside the file + * @throws FormatterException if the upper endpoint of the range is outside the file */ - Range<Integer> characterRangeToTokenRange(int offset, int length) throws FormatterException { - int requiredLength = offset + length; - if (requiredLength > text.length()) { + Range<Integer> characterRangeToTokenRange(Range<Integer> characterRange) + throws FormatterException { + if (characterRange.upperEndpoint() > text.length()) { throw new FormatterException( String.format( "error: invalid length %d, offset + length (%d) is outside the file", - length, requiredLength)); - } - if (length < 0) { - return EMPTY_RANGE; - } - if (length == 0) { - // 0 stands for "format the line under the cursor" - length = 1; - } + characterRange.upperEndpoint() - characterRange.lowerEndpoint(), + characterRange.upperEndpoint())); + } + // empty range stands for "format the line under the cursor" + Range<Integer> nonEmptyRange = + characterRange.isEmpty() + ? Range.closedOpen(characterRange.lowerEndpoint(), characterRange.lowerEndpoint() + 1) + : characterRange; ImmutableCollection<Token> enclosed = - getPositionTokenMap() - .subRangeMap(Range.closedOpen(offset, offset + length)) - .asMapOfRanges() - .values(); + getPositionTokenMap().subRangeMap(nonEmptyRange).asMapOfRanges().values(); if (enclosed.isEmpty()) { return EMPTY_RANGE; } @@ -594,7 +589,7 @@ public final class JavaInput extends Input { /** * Get the number of toks. * - * @return the number of toks, including the EOF tok + * @return the number of toks, excluding the EOF tok */ @Override public int getkN() { @@ -604,7 +599,7 @@ public final class JavaInput extends Input { /** * Get the Token by index. * - * @param k the token index + * @param k the Tok index */ @Override public Token getToken(int k) { @@ -664,12 +659,9 @@ public final class JavaInput extends Input { public RangeSet<Integer> characterRangesToTokenRanges(Collection<Range<Integer>> characterRanges) throws FormatterException { RangeSet<Integer> tokenRangeSet = TreeRangeSet.create(); - for (Range<Integer> characterRange0 : characterRanges) { - Range<Integer> characterRange = characterRange0.canonical(DiscreteDomain.integers()); + for (Range<Integer> characterRange : characterRanges) { tokenRangeSet.add( - characterRangeToTokenRange( - characterRange.lowerEndpoint(), - characterRange.upperEndpoint() - characterRange.lowerEndpoint())); + characterRangeToTokenRange(characterRange.canonical(DiscreteDomain.integers()))); } return tokenRangeSet; } 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 6ce0f66..daed250 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -43,19 +43,27 @@ import static com.sun.source.tree.Tree.Kind.UNION_TYPE; import static com.sun.source.tree.Tree.Kind.VARIABLE; import static java.util.stream.Collectors.toList; +import com.google.auto.value.AutoOneOf; +import com.google.auto.value.AutoValue; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; import com.google.common.base.Throwables; import com.google.common.base.Verify; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Multiset; import com.google.common.collect.PeekingIterator; +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; import com.google.common.collect.Streams; +import com.google.common.collect.TreeRangeSet; +import com.google.errorprone.annotations.CheckReturnValue; import com.google.googlejavaformat.CloseOp; import com.google.googlejavaformat.Doc; import com.google.googlejavaformat.Doc.FillMode; @@ -139,8 +147,9 @@ import com.sun.tools.javac.tree.TreeScanner; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.Comparator; import java.util.Deque; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -168,7 +177,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Whether to break or not. */ - enum BreakOrNot { + protected enum BreakOrNot { YES, NO; @@ -178,7 +187,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Whether to collapse empty blocks. */ - enum CollapseEmptyOrNot { + protected enum CollapseEmptyOrNot { YES, NO; @@ -192,7 +201,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Whether to allow leading blank lines in blocks. */ - enum AllowLeadingBlankLine { + protected enum AllowLeadingBlankLine { YES, NO; @@ -202,7 +211,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Whether to allow trailing blank lines in blocks. */ - enum AllowTrailingBlankLine { + protected enum AllowTrailingBlankLine { YES, NO; @@ -269,6 +278,21 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } + // TODO(cushon): generalize this + private static final ImmutableMultimap<String, String> TYPE_ANNOTATIONS = typeAnnotations(); + + private static ImmutableSetMultimap<String, String> typeAnnotations() { + ImmutableSetMultimap.Builder<String, String> result = ImmutableSetMultimap.builder(); + for (String annotation : + ImmutableList.of( + "org.jspecify.nullness.Nullable", + "org.checkerframework.checker.nullness.qual.Nullable")) { + String simpleName = annotation.substring(annotation.lastIndexOf('.') + 1); + result.put(simpleName, annotation); + } + return result.build(); + } + protected final OpsBuilder builder; protected static final Indent.Const ZERO = Indent.Const.ZERO; @@ -278,6 +302,8 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { protected final Indent.Const plusTwo; protected final Indent.Const plusFour; + private final Set<Name> typeAnnotationSimpleNames = new HashSet<>(); + private static final ImmutableList<Op> breakList(Optional<BreakTag> breakTag) { return ImmutableList.of(Doc.Break.make(Doc.FillMode.UNIFIED, " ", ZERO, breakTag)); } @@ -293,8 +319,6 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { return ImmutableList.of(Doc.Break.make(FillMode.FORCED, "", Indent.Const.ZERO, breakTag)); } - private static final ImmutableList<Op> EMPTY_LIST = ImmutableList.of(); - /** * Allow multi-line filling (of array initializers, argument lists, and boolean expressions) for * items with length less than or equal to this threshold. @@ -377,11 +401,14 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { first = false; dropEmptyDeclarations(); } + handleModule(first, node); // set a partial format marker at EOF to make sure we can format the entire file markForPartialFormat(); return null; } + protected void handleModule(boolean first, CompilationUnitTree node) {} + /** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */ protected void dropEmptyDeclarations() { if (builder.peekToken().equals(Optional.of(";"))) { @@ -415,10 +442,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { public void visitAnnotationType(ClassTree node) { sync(node); builder.open(ZERO); - visitAndBreakModifiers( - node.getModifiers(), - Direction.VERTICAL, - /* declarationAnnotationBreak= */ Optional.empty()); + typeDeclarationModifiers(node.getModifiers()); builder.open(ZERO); token("@"); token("interface"); @@ -677,9 +701,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { builder.space(); addTypeArguments(node.getTypeArguments(), plusFour); if (node.getClassBody() != null) { - builder.addAll( + List<AnnotationTree> annotations = visitModifiers( - node.getClassBody().getModifiers(), Direction.HORIZONTAL, Optional.empty())); + node.getClassBody().getModifiers(), Direction.HORIZONTAL, Optional.empty()); + visitAnnotations(annotations, BreakOrNot.NO, BreakOrNot.YES); } scan(node.getIdentifier(), null); addArguments(node.getArguments(), plusFour); @@ -800,10 +825,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { public boolean visitEnumDeclaration(ClassTree node) { sync(node); builder.open(ZERO); - visitAndBreakModifiers( - node.getModifiers(), - Direction.VERTICAL, - /* declarationAnnotationBreak= */ Optional.empty()); + typeDeclarationModifiers(node.getModifiers()); builder.open(plusFour); token("enum"); builder.breakOp(" "); @@ -967,7 +989,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } - private TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) { + private static TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) { if (type == null) { return null; } @@ -1112,6 +1134,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { @Override public Void visitImport(ImportTree node, Void unused) { + checkForTypeAnnotation(node); sync(node); token("import"); builder.space(); @@ -1126,6 +1149,21 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { return null; } + private void checkForTypeAnnotation(ImportTree node) { + Name simpleName = getSimpleName(node); + Collection<String> wellKnownAnnotations = TYPE_ANNOTATIONS.get(simpleName.toString()); + if (!wellKnownAnnotations.isEmpty() + && wellKnownAnnotations.contains(node.getQualifiedIdentifier().toString())) { + typeAnnotationSimpleNames.add(simpleName); + } + } + + private static Name getSimpleName(ImportTree importTree) { + return importTree.getQualifiedIdentifier() instanceof IdentifierTree + ? ((IdentifierTree) importTree.getQualifiedIdentifier()).getName() + : ((MemberSelectTree) importTree.getQualifiedIdentifier()).getIdentifier(); + } + @Override public Void visitBinary(BinaryTree node, Void unused) { sync(node); @@ -1358,9 +1396,12 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } } - builder.addAll( + List<AnnotationTree> typeAnnotations = visitModifiers( - annotations, Direction.VERTICAL, /* declarationAnnotationBreak= */ Optional.empty())); + node.getModifiers(), + annotations, + Direction.VERTICAL, + /* declarationAnnotationBreak= */ Optional.empty()); Tree baseReturnType = null; Deque<List<? extends AnnotationTree>> dims = null; @@ -1369,6 +1410,9 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { DimensionHelpers.extractDims(node.getReturnType(), SortedDims.YES); baseReturnType = extractedDims.node; dims = new ArrayDeque<>(extractedDims.dims); + } else { + verticalAnnotations(typeAnnotations); + typeAnnotations = ImmutableList.of(); } builder.open(plusFour); @@ -1377,7 +1421,14 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { builder.open(ZERO); { boolean first = true; + if (!typeAnnotations.isEmpty()) { + visitAnnotations(typeAnnotations, BreakOrNot.NO, BreakOrNot.NO); + first = false; + } if (!node.getTypeParameters().isEmpty()) { + if (!first) { + builder.breakToFill(" "); + } token("<"); typeParametersRest(node.getTypeParameters(), plusFour); if (!returnTypeAnnotations.isEmpty()) { @@ -1600,11 +1651,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { public Void visitLiteral(LiteralTree node, Void unused) { sync(node); String sourceForNode = getSourceForNode(node, getCurrentPath()); - // A negative numeric literal -n is usually represented as unary minus on n, - // but that doesn't work for integer or long MIN_VALUE. The parser works - // around that by representing it directly as a signed literal (with no - // unary minus), but the lexer still expects two tokens. - if (sourceForNode.startsWith("-")) { + if (isUnaryMinusLiteral(sourceForNode)) { token("-"); sourceForNode = sourceForNode.substring(1).trim(); } @@ -1612,6 +1659,14 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { return null; } + // A negative numeric literal -n is usually represented as unary minus on n, + // but that doesn't work for integer or long MIN_VALUE. The parser works + // around that by representing it directly as a signed literal (with no + // unary minus), but the lexer still expects two tokens. + private static boolean isUnaryMinusLiteral(String literalTreeSource) { + return literalTreeSource.startsWith("-"); + } + private void visitPackage( ExpressionTree packageName, List<? extends AnnotationTree> packageAnnotations) { if (!packageAnnotations.isEmpty()) { @@ -1697,10 +1752,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { default: return false; } - if (!(node.getExpression() instanceof UnaryTree)) { + JCTree.Tag tag = unaryTag(node.getExpression()); + if (tag == null) { return false; } - JCTree.Tag tag = ((JCTree) node.getExpression()).getTag(); if (tag.isPostUnaryOp()) { return false; } @@ -1710,6 +1765,17 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { return true; } + private JCTree.Tag unaryTag(ExpressionTree expression) { + if (expression instanceof UnaryTree) { + return ((JCTree) expression).getTag(); + } + if (expression instanceof LiteralTree + && isUnaryMinusLiteral(getSourceForNode(expression, getCurrentPath()))) { + return JCTree.Tag.MINUS; + } + return null; + } + @Override public Void visitPrimitiveType(PrimitiveTypeTree node, Void unused) { sync(node); @@ -1939,14 +2005,11 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { public void visitClassDeclaration(ClassTree node) { sync(node); - List<Op> breaks = - visitModifiers( - node.getModifiers(), - Direction.VERTICAL, - /* declarationAnnotationBreak= */ Optional.empty()); + typeDeclarationModifiers(node.getModifiers()); + List<? extends Tree> permitsTypes = getPermitsClause(node); boolean hasSuperclassType = node.getExtendsClause() != null; boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); - builder.addAll(breaks); + boolean hasPermitsTypes = !permitsTypes.isEmpty(); token(node.getKind() == Tree.Kind.INTERFACE ? "interface" : "class"); builder.space(); visit(node.getSimpleName()); @@ -1958,7 +2021,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { if (!node.getTypeParameters().isEmpty()) { typeParametersRest( node.getTypeParameters(), - hasSuperclassType || hasSuperInterfaceTypes ? plusFour : ZERO); + hasSuperclassType || hasSuperInterfaceTypes || hasPermitsTypes ? plusFour : ZERO); } if (hasSuperclassType) { builder.breakToFill(" "); @@ -1966,22 +2029,10 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { builder.space(); scan(node.getExtendsClause(), null); } - if (hasSuperInterfaceTypes) { - builder.breakToFill(" "); - builder.open(node.getImplementsClause().size() > 1 ? plusFour : ZERO); - token(node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements"); - builder.space(); - boolean first = true; - for (Tree superInterfaceType : node.getImplementsClause()) { - if (!first) { - token(","); - builder.breakOp(" "); - } - scan(superInterfaceType, null); - first = false; - } - builder.close(); - } + classDeclarationTypeList( + node.getKind() == Tree.Kind.INTERFACE ? "extends" : "implements", + node.getImplementsClause()); + classDeclarationTypeList("permits", permitsTypes); } builder.close(); if (node.getMembers() == null) { @@ -2062,7 +2113,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { // Helper methods. /** Helper method for annotations. */ - void visitAnnotations( + protected void visitAnnotations( List<? extends AnnotationTree> annotations, BreakOrNot breakBefore, BreakOrNot breakAfter) { if (!annotations.isEmpty()) { if (breakBefore.isYes()) { @@ -2082,8 +2133,16 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } + void verticalAnnotations(List<AnnotationTree> annotations) { + for (AnnotationTree annotation : annotations) { + builder.forcedBreak(); + scan(annotation, null); + builder.forcedBreak(); + } + } + /** Helper method for blocks. */ - private void visitBlock( + protected void visitBlock( BlockTree node, CollapseEmptyOrNot collapseEmptyOrNot, AllowLeadingBlankLine allowLeadingBlankLine, @@ -2169,12 +2228,21 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } + protected void typeDeclarationModifiers(ModifiersTree modifiers) { + List<AnnotationTree> typeAnnotations = + visitModifiers( + modifiers, Direction.VERTICAL, /* declarationAnnotationBreak= */ Optional.empty()); + verticalAnnotations(typeAnnotations); + } + /** Output combined modifiers and annotations and the trailing break. */ void visitAndBreakModifiers( ModifiersTree modifiers, Direction annotationDirection, Optional<BreakTag> declarationAnnotationBreak) { - builder.addAll(visitModifiers(modifiers, annotationDirection, declarationAnnotationBreak)); + List<AnnotationTree> typeAnnotations = + visitModifiers(modifiers, annotationDirection, declarationAnnotationBreak); + visitAnnotations(typeAnnotations, BreakOrNot.NO, BreakOrNot.YES); } @Override @@ -2183,36 +2251,50 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Output combined modifiers and annotations and returns the trailing break. */ - protected List<Op> visitModifiers( + @CheckReturnValue + protected ImmutableList<AnnotationTree> visitModifiers( ModifiersTree modifiersTree, Direction annotationsDirection, Optional<BreakTag> declarationAnnotationBreak) { return visitModifiers( - modifiersTree.getAnnotations(), annotationsDirection, declarationAnnotationBreak); + modifiersTree, + modifiersTree.getAnnotations(), + annotationsDirection, + declarationAnnotationBreak); } - protected List<Op> visitModifiers( + @CheckReturnValue + protected ImmutableList<AnnotationTree> visitModifiers( + ModifiersTree modifiersTree, List<? extends AnnotationTree> annotationTrees, Direction annotationsDirection, Optional<BreakTag> declarationAnnotationBreak) { - if (annotationTrees.isEmpty() && !nextIsModifier()) { - return EMPTY_LIST; + DeclarationModifiersAndTypeAnnotations splitModifiers = + splitModifiers(modifiersTree, annotationTrees); + return visitModifiers(splitModifiers, annotationsDirection, declarationAnnotationBreak); + } + + @CheckReturnValue + private ImmutableList<AnnotationTree> visitModifiers( + DeclarationModifiersAndTypeAnnotations splitModifiers, + Direction annotationsDirection, + Optional<BreakTag> declarationAnnotationBreak) { + if (splitModifiers.declarationModifiers().isEmpty()) { + return splitModifiers.typeAnnotations(); } - Deque<AnnotationTree> annotations = new ArrayDeque<>(annotationTrees); + Deque<AnnotationOrModifier> declarationModifiers = + new ArrayDeque<>(splitModifiers.declarationModifiers()); builder.open(ZERO); boolean first = true; boolean lastWasAnnotation = false; - while (!annotations.isEmpty()) { - if (nextIsModifier()) { - break; - } + while (!declarationModifiers.isEmpty() && !declarationModifiers.peekFirst().isModifier()) { if (!first) { builder.addAll( annotationsDirection.isVertical() ? forceBreakList(declarationAnnotationBreak) : breakList(declarationAnnotationBreak)); } - scan(annotations.removeFirst(), null); + formatAnnotationOrModifier(declarationModifiers); first = false; lastWasAnnotation = true; } @@ -2221,8 +2303,9 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { annotationsDirection.isVertical() ? forceBreakList(declarationAnnotationBreak) : breakList(declarationAnnotationBreak); - if (annotations.isEmpty() && !nextIsModifier()) { - return trailingBreak; + if (declarationModifiers.isEmpty()) { + builder.addAll(trailingBreak); + return splitModifiers.typeAnnotations(); } if (lastWasAnnotation) { builder.addAll(trailingBreak); @@ -2230,24 +2313,171 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { builder.open(ZERO); first = true; - while (nextIsModifier() || !annotations.isEmpty()) { + while (!declarationModifiers.isEmpty()) { if (!first) { builder.addAll(breakFillList(Optional.empty())); } - if (nextIsModifier()) { - token(builder.peekToken().get()); - } else { - scan(annotations.removeFirst(), null); - lastWasAnnotation = true; - } + formatAnnotationOrModifier(declarationModifiers); first = false; } builder.close(); - return breakFillList(Optional.empty()); + builder.addAll(breakFillList(Optional.empty())); + return splitModifiers.typeAnnotations(); + } + + /** Represents an annotation or a modifier in a {@link ModifiersTree}. */ + @AutoOneOf(AnnotationOrModifier.Kind.class) + abstract static class AnnotationOrModifier implements Comparable<AnnotationOrModifier> { + enum Kind { + MODIFIER, + ANNOTATION + } + + abstract Kind getKind(); + + abstract AnnotationTree annotation(); + + abstract Input.Tok modifier(); + + static AnnotationOrModifier ofModifier(Input.Tok m) { + return AutoOneOf_JavaInputAstVisitor_AnnotationOrModifier.modifier(m); + } + + static AnnotationOrModifier ofAnnotation(AnnotationTree a) { + return AutoOneOf_JavaInputAstVisitor_AnnotationOrModifier.annotation(a); + } + + boolean isModifier() { + return getKind().equals(Kind.MODIFIER); + } + + boolean isAnnotation() { + return getKind().equals(Kind.ANNOTATION); + } + + int position() { + switch (getKind()) { + case MODIFIER: + return modifier().getPosition(); + case ANNOTATION: + return getStartPosition(annotation()); + } + throw new AssertionError(); + } + + private static final Comparator<AnnotationOrModifier> COMPARATOR = + Comparator.comparingInt(AnnotationOrModifier::position); + + @Override + public int compareTo(AnnotationOrModifier o) { + return COMPARATOR.compare(this, o); + } + } + + /** + * The modifiers annotations for a declaration, grouped in to a prefix that contains all of the + * declaration annotations and modifiers, and a suffix of type annotations. + * + * <p>For examples like {@code @Deprecated public @Nullable Foo foo();}, this allows us to format + * {@code @Deprecated public} as declaration modifiers, and {@code @Nullable} as a type annotation + * on the return type. + */ + @AutoValue + abstract static class DeclarationModifiersAndTypeAnnotations { + abstract ImmutableList<AnnotationOrModifier> declarationModifiers(); + + abstract ImmutableList<AnnotationTree> typeAnnotations(); + + static DeclarationModifiersAndTypeAnnotations create( + ImmutableList<AnnotationOrModifier> declarationModifiers, + ImmutableList<AnnotationTree> typeAnnotations) { + return new AutoValue_JavaInputAstVisitor_DeclarationModifiersAndTypeAnnotations( + declarationModifiers, typeAnnotations); + } + + static DeclarationModifiersAndTypeAnnotations empty() { + return create(ImmutableList.of(), ImmutableList.of()); + } + + boolean hasDeclarationAnnotation() { + return declarationModifiers().stream().anyMatch(AnnotationOrModifier::isAnnotation); + } + } + + /** + * Examines the token stream to convert the modifiers for a declaration into a {@link + * DeclarationModifiersAndTypeAnnotations}. + */ + DeclarationModifiersAndTypeAnnotations splitModifiers( + ModifiersTree modifiersTree, List<? extends AnnotationTree> annotations) { + if (annotations.isEmpty() && !isModifier(builder.peekToken().get())) { + return DeclarationModifiersAndTypeAnnotations.empty(); + } + RangeSet<Integer> annotationRanges = TreeRangeSet.create(); + for (AnnotationTree annotationTree : annotations) { + annotationRanges.add( + Range.closedOpen( + getStartPosition(annotationTree), getEndPosition(annotationTree, getCurrentPath()))); + } + ImmutableList<Input.Tok> toks = + builder.peekTokens( + getStartPosition(modifiersTree), + (Input.Tok tok) -> + // ModifiersTree end position information isn't reliable, so scan tokens as long as + // we're seeing annotations or modifiers + annotationRanges.contains(tok.getPosition()) || isModifier(tok.getText())); + ImmutableList<AnnotationOrModifier> modifiers = + ImmutableList.copyOf( + Streams.concat( + toks.stream() + // reject tokens from inside AnnotationTrees, we only want modifiers + .filter(t -> !annotationRanges.contains(t.getPosition())) + .map(AnnotationOrModifier::ofModifier), + annotations.stream().map(AnnotationOrModifier::ofAnnotation)) + .sorted() + .collect(toList())); + // Take a suffix of annotations that are well-known type annotations, and which appear after any + // declaration annotations or modifiers + ImmutableList.Builder<AnnotationTree> typeAnnotations = ImmutableList.builder(); + int idx = modifiers.size() - 1; + while (idx >= 0) { + AnnotationOrModifier modifier = modifiers.get(idx); + if (!modifier.isAnnotation() || !isTypeAnnotation(modifier.annotation())) { + break; + } + typeAnnotations.add(modifier.annotation()); + idx--; + } + return DeclarationModifiersAndTypeAnnotations.create( + modifiers.subList(0, idx + 1), typeAnnotations.build().reverse()); } - boolean nextIsModifier() { - switch (builder.peekToken().get()) { + private void formatAnnotationOrModifier(Deque<AnnotationOrModifier> modifiers) { + AnnotationOrModifier modifier = modifiers.removeFirst(); + switch (modifier.getKind()) { + case MODIFIER: + token(modifier.modifier().getText()); + if (modifier.modifier().getText().equals("non")) { + token(modifiers.removeFirst().modifier().getText()); + token(modifiers.removeFirst().modifier().getText()); + } + break; + case ANNOTATION: + scan(modifier.annotation(), null); + break; + } + } + + boolean isTypeAnnotation(AnnotationTree annotationTree) { + Tree annotationType = annotationTree.getAnnotationType(); + if (!(annotationType instanceof IdentifierTree)) { + return false; + } + return typeAnnotationSimpleNames.contains(((IdentifierTree) annotationType).getName()); + } + + private static boolean isModifier(String token) { + switch (token) { case "public": case "protected": case "private": @@ -2260,6 +2490,9 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { case "native": case "strictfp": case "default": + case "sealed": + case "non": + case "-": return true; default: return false; @@ -2366,7 +2599,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { builder.open(ZERO); boolean first = true; if (receiver.isPresent()) { - // TODO(jdd): Use builders. + // TODO(user): Use builders. declareOne( DeclarationKind.PARAMETER, Direction.HORIZONTAL, @@ -2868,7 +3101,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } /** Returns the simple names of expressions in a "." chain. */ - private List<String> simpleNames(Deque<ExpressionTree> stack) { + private static ImmutableList<String> simpleNames(Deque<ExpressionTree> stack) { ImmutableList.Builder<String> simpleNames = ImmutableList.builder(); OUTER: for (ExpressionTree expression : stack) { @@ -2906,7 +3139,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { if (!methodInvocation.getTypeArguments().isEmpty()) { builder.open(plusFour); addTypeArguments(methodInvocation.getTypeArguments(), ZERO); - // TODO(jdd): Should indent the name -4. + // TODO(user): Should indent the name -4. builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, tyargTag); builder.close(); } @@ -2925,14 +3158,14 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { * Returns the base expression of an erray access, e.g. given {@code foo[0][0]} returns {@code * foo}. */ - private ExpressionTree getArrayBase(ExpressionTree node) { + private static ExpressionTree getArrayBase(ExpressionTree node) { while (node instanceof ArrayAccessTree) { node = ((ArrayAccessTree) node).getExpression(); } return node; } - private ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { + private static ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { ExpressionTree select = methodInvocation.getMethodSelect(); return select instanceof MemberSelectTree ? ((MemberSelectTree) select).getExpression() : null; } @@ -2973,7 +3206,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { * Returns all array indices for the given expression, e.g. given {@code foo[0][0]} returns the * expressions for {@code [0][0]}. */ - private Deque<ExpressionTree> getArrayIndices(ExpressionTree expression) { + private static Deque<ExpressionTree> getArrayIndices(ExpressionTree expression) { Deque<ExpressionTree> indices = new ArrayDeque<>(); while (expression instanceof ArrayAccessTree) { ArrayAccessTree array = (ArrayAccessTree) expression; @@ -3270,20 +3503,25 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } Deque<List<? extends AnnotationTree>> dims = - new ArrayDeque<>( - typeWithDims.isPresent() ? typeWithDims.get().dims : Collections.emptyList()); + new ArrayDeque<>(typeWithDims.isPresent() ? typeWithDims.get().dims : ImmutableList.of()); int baseDims = 0; + // preprocess to separate declaration annotations + modifiers, type annotations + + DeclarationModifiersAndTypeAnnotations declarationAndTypeModifiers = + modifiers + .map(m -> splitModifiers(m, m.getAnnotations())) + .orElse(DeclarationModifiersAndTypeAnnotations.empty()); builder.open( - kind == DeclarationKind.PARAMETER - && (modifiers.isPresent() && !modifiers.get().getAnnotations().isEmpty()) + kind == DeclarationKind.PARAMETER && declarationAndTypeModifiers.hasDeclarationAnnotation() ? plusFour : ZERO); { - if (modifiers.isPresent()) { - visitAndBreakModifiers( - modifiers.get(), annotationsDirection, Optional.of(verticalAnnotationBreak)); - } + List<AnnotationTree> annotations = + visitModifiers( + declarationAndTypeModifiers, + annotationsDirection, + Optional.of(verticalAnnotationBreak)); boolean isVar = builder.peekToken().get().equals("var") && (!name.contentEquals("var") || builder.peekToken(1).get().equals("var")); @@ -3294,6 +3532,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { { builder.open(ZERO); { + visitAnnotations(annotations, BreakOrNot.NO, BreakOrNot.YES); if (typeWithDims.isPresent() && typeWithDims.get().node != null) { scan(typeWithDims.get().node, null); int totalDims = dims.size(); @@ -3533,6 +3772,31 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { } } + /** Gets the permits clause for the given node. This is only available in Java 15 and later. */ + protected List<? extends Tree> getPermitsClause(ClassTree node) { + return ImmutableList.of(); + } + + private void classDeclarationTypeList(String token, List<? extends Tree> types) { + if (types.isEmpty()) { + return; + } + builder.breakToFill(" "); + builder.open(types.size() > 1 ? plusFour : ZERO); + token(token); + builder.space(); + boolean first = true; + for (Tree type : types) { + if (!first) { + token(","); + builder.breakOp(" "); + } + scan(type, null); + first = false; + } + builder.close(); + } + /** * The parser expands multi-variable declarations into separate single-variable declarations. All * of the fragments in the original declaration have the same start position, so we use that as a @@ -3540,7 +3804,8 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { * * <p>e.g. {@code int x, y;} is parsed as {@code int x; int y;}. */ - private List<VariableTree> variableFragments(PeekingIterator<? extends Tree> it, Tree first) { + private static List<VariableTree> variableFragments( + PeekingIterator<? extends Tree> it, Tree first) { List<VariableTree> fragments = new ArrayList<>(); if (first.getKind() == VARIABLE) { int start = getStartPosition(first); @@ -3590,7 +3855,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { * @param modifiers the list of {@link ModifiersTree}s * @return whether the local can be declared with horizontal annotations */ - private Direction canLocalHaveHorizontalAnnotations(ModifiersTree modifiers) { + private static Direction canLocalHaveHorizontalAnnotations(ModifiersTree modifiers) { int parameterlessAnnotations = 0; for (AnnotationTree annotation : modifiers.getAnnotations()) { if (annotation.getArguments().isEmpty()) { @@ -3607,7 +3872,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> { * Should a field with a set of modifiers be declared with horizontal annotations? This is * currently true if all annotations are parameterless annotations. */ - private Direction fieldAnnotationDirection(ModifiersTree modifiers) { + private static Direction fieldAnnotationDirection(ModifiersTree modifiers) { for (AnnotationTree annotation : modifiers.getAnnotations()) { if (!annotation.getArguments().isEmpty()) { return Direction.VERTICAL; 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 c059318..c43a91a 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaOutput.java @@ -14,6 +14,7 @@ package com.google.googlejavaformat.java; +import static java.lang.Math.min; import static java.util.Comparator.comparing; import com.google.common.base.CharMatcher; @@ -89,7 +90,7 @@ public final class JavaOutput extends Output { partialFormatRanges.add(Range.closed(lo, hi)); } - // TODO(jdd): Add invariant. + // TODO(user): Add invariant. @Override public void append(String text, Range<Integer> range) { if (!range.isEmpty()) { @@ -261,8 +262,7 @@ public final class JavaOutput extends Output { } } - int replaceTo = - Math.min(endTok.getPosition() + endTok.length(), javaInput.getText().length()); + int replaceTo = min(endTok.getPosition() + endTok.length(), javaInput.getText().length()); // If the formatted ranged ended in the trailing trivia of the last token before EOF, // format all the way up to EOF to deal with trailing whitespace correctly. if (endTok.getIndex() == javaInput.getkN() - 1) { @@ -304,7 +304,7 @@ public final class JavaOutput extends Output { } else { if (newline == -1) { // If there wasn't a trailing newline in the input, indent the next line. - replacement.append(after.substring(0, idx)); + replacement.append(after, 0, idx); } break; } @@ -352,7 +352,7 @@ public final class JavaOutput extends Output { public static int startPosition(Token token) { int min = token.getTok().getPosition(); for (Input.Tok tok : token.getToksBefore()) { - min = Math.min(min, tok.getPosition()); + min = min(min, tok.getPosition()); } return min; } 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 a8c9efd..ba7e3b7 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavacTokens.java @@ -128,10 +128,28 @@ class JavacTokens { @Override protected Comment processComment(int pos, int endPos, CommentStyle style) { - char[] buf = reader.getRawCharacters(pos, endPos); + char[] buf = getRawCharactersReflectively(pos, endPos); return new CommentWithTextAndPosition( pos, endPos, new AccessibleReader(fac, buf, buf.length), style); } + + private char[] getRawCharactersReflectively(int beginIndex, int endIndex) { + Object instance; + try { + instance = JavaTokenizer.class.getDeclaredField("reader").get(this); + } catch (ReflectiveOperationException e) { + instance = this; + } + try { + return (char[]) + instance + .getClass() + .getMethod("getRawCharacters", int.class, int.class) + .invoke(instance, beginIndex, endIndex); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } } /** A {@link Comment} that saves its text and start position. */ 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 9231bda..953ca58 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Main.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Main.java @@ -14,6 +14,7 @@ package com.google.googlejavaformat.java; +import static java.lang.Math.min; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.io.ByteStreams; @@ -40,7 +41,7 @@ public final class Main { private static final int MAX_THREADS = 20; private static final String STDIN_FILENAME = "<stdin>"; - static final String versionString() { + static String versionString() { return "google-java-format: Version " + GoogleJavaFormatVersion.version(); } @@ -62,20 +63,27 @@ public final class Main { * @param args the command-line arguments */ public static void main(String[] args) { - int result; 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); + System.exit(result); + } + + /** + * Package-private main entry point used this CLI program and the java.util.spi.ToolProvider + * implementation in the same package as this Main class. + */ + static int main(PrintWriter out, PrintWriter err, String... args) { try { Main formatter = new Main(out, err, System.in); - result = formatter.format(args); + return formatter.format(args); } catch (UsageException e) { err.print(e.getMessage()); - result = 0; + return 0; } finally { err.flush(); out.flush(); } - System.exit(result); } /** @@ -109,7 +117,7 @@ public final class Main { } private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions options) { - int numThreads = Math.min(MAX_THREADS, parameters.files().size()); + int numThreads = min(MAX_THREADS, parameters.files().size()); ExecutorService executorService = Executors.newFixedThreadPool(numThreads); Map<Path, String> inputs = new LinkedHashMap<>(); @@ -146,7 +154,7 @@ public final class Main { } catch (ExecutionException e) { if (e.getCause() instanceof FormatterException) { for (FormatterDiagnostic diagnostic : ((FormatterException) e.getCause()).diagnostics()) { - errWriter.println(path + ":" + diagnostic.toString()); + errWriter.println(path + ":" + diagnostic); } } else { errWriter.println(path + ": error: " + e.getCause().getMessage()); @@ -205,7 +213,7 @@ public final class Main { } } catch (FormatterException e) { for (FormatterDiagnostic diagnostic : e.diagnostics()) { - errWriter.println(stdinFilename + ":" + diagnostic.toString()); + errWriter.println(stdinFilename + ":" + diagnostic); } ok = false; // TODO(cpovirk): Catch other types of exception (as we do in the formatFiles case). diff --git a/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java b/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java index f7f610b..e14b290 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java +++ b/core/src/main/java/com/google/googlejavaformat/java/ModifierOrderer.java @@ -36,44 +36,6 @@ import javax.lang.model.element.Modifier; /** Fixes sequences of modifiers to be in JLS order. */ final class ModifierOrderer { - /** - * Returns the {@link javax.lang.model.element.Modifier} for the given token kind, or {@code - * null}. - */ - private static Modifier getModifier(TokenKind kind) { - if (kind == null) { - return null; - } - switch (kind) { - case PUBLIC: - return Modifier.PUBLIC; - case PROTECTED: - return Modifier.PROTECTED; - case PRIVATE: - return Modifier.PRIVATE; - case ABSTRACT: - return Modifier.ABSTRACT; - case STATIC: - return Modifier.STATIC; - case DEFAULT: - return Modifier.DEFAULT; - case FINAL: - return Modifier.FINAL; - case TRANSIENT: - return Modifier.TRANSIENT; - case VOLATILE: - return Modifier.VOLATILE; - case SYNCHRONIZED: - return Modifier.SYNCHRONIZED; - case NATIVE: - return Modifier.NATIVE; - case STRICTFP: - return Modifier.STRICTFP; - default: - return null; - } - } - /** Reorders all modifiers in the given text to be in JLS order. */ static JavaInput reorderModifiers(String text) throws FormatterException { return reorderModifiers( @@ -130,7 +92,7 @@ final class ModifierOrderer { if (i > 0) { addTrivia(replacement, modifierTokens.get(i).getToksBefore()); } - replacement.append(mods.get(i).toString()); + replacement.append(mods.get(i)); if (i < (modifierTokens.size() - 1)) { addTrivia(replacement, modifierTokens.get(i).getToksAfter()); } @@ -152,7 +114,44 @@ final class ModifierOrderer { * is not a modifier. */ private static Modifier asModifier(Token token) { - return getModifier(((JavaInput.Tok) token.getTok()).kind()); + TokenKind kind = ((JavaInput.Tok) token.getTok()).kind(); + if (kind != null) { + switch (kind) { + case PUBLIC: + return Modifier.PUBLIC; + case PROTECTED: + return Modifier.PROTECTED; + case PRIVATE: + return Modifier.PRIVATE; + case ABSTRACT: + return Modifier.ABSTRACT; + case STATIC: + return Modifier.STATIC; + case DEFAULT: + return Modifier.DEFAULT; + case FINAL: + return Modifier.FINAL; + case TRANSIENT: + return Modifier.TRANSIENT; + case VOLATILE: + return Modifier.VOLATILE; + case SYNCHRONIZED: + return Modifier.SYNCHRONIZED; + case NATIVE: + return Modifier.NATIVE; + case STRICTFP: + return Modifier.STRICTFP; + default: // fall out + } + } + switch (token.getTok().getText()) { + case "non-sealed": + return Modifier.valueOf("NON_SEALED"); + case "sealed": + return Modifier.valueOf("SEALED"); + default: + return null; + } } /** Applies replacements to the given string. */ 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 d939480..20e55e9 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java +++ b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java @@ -16,6 +16,7 @@ package com.google.googlejavaformat.java; +import static java.lang.Math.max; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.CharMatcher; @@ -31,6 +32,7 @@ import com.google.common.collect.TreeRangeSet; import com.google.googlejavaformat.Newlines; import com.sun.source.doctree.DocCommentTree; import com.sun.source.doctree.ReferenceTree; +import com.sun.source.tree.CaseTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.Tree; @@ -54,8 +56,10 @@ import com.sun.tools.javac.util.Log; 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.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import javax.tools.Diagnostic; @@ -114,6 +118,31 @@ public class RemoveUnusedImports { return null; } + // TODO(cushon): remove this override when pattern matching in switch is no longer a preview + // feature, and TreePathScanner visits CaseTree#getLabels instead of CaseTree#getExpressions + @SuppressWarnings("unchecked") // reflection + @Override + public Void visitCase(CaseTree tree, Void unused) { + if (CASE_TREE_GET_LABELS != null) { + try { + scan((List<? extends Tree>) CASE_TREE_GET_LABELS.invoke(tree), null); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + return super.visitCase(tree, null); + } + + private static final Method CASE_TREE_GET_LABELS = caseTreeGetLabels(); + + private static Method caseTreeGetLabels() { + try { + return CaseTree.class.getMethod("getLabels"); + } catch (NoSuchMethodException e) { + return null; + } + } + @Override public Void scan(Tree tree, Void unused) { if (tree == null) { @@ -145,7 +174,9 @@ public class RemoveUnusedImports { public Void visitReference(ReferenceTree referenceTree, Void unused) { DCReference reference = (DCReference) referenceTree; long basePos = - reference.getSourcePosition((DCTree.DCDocComment) getCurrentPath().getDocComment()); + reference + .pos((DCTree.DCDocComment) getCurrentPath().getDocComment()) + .getStartPosition(); // the position of trees inside the reference node aren't stored, but the qualifier's // start position is the beginning of the reference node if (reference.qualifierExpression != null) { @@ -247,7 +278,7 @@ public class RemoveUnusedImports { } // delete the import int endPosition = importTree.getEndPosition(unit.endPositions); - endPosition = Math.max(CharMatcher.isNot(' ').indexIn(contents, endPosition), endPosition); + endPosition = max(CharMatcher.isNot(' ').indexIn(contents, endPosition), endPosition); String sep = Newlines.guessLineSeparator(contents); if (endPosition + sep.length() < contents.length() && contents.subSequence(endPosition, endPosition + sep.length()).toString().equals(sep)) { 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 e41bb66..c0f16e9 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -239,9 +239,10 @@ public final class StringWrapper { * @param separator the line separator * @param columnLimit the number of columns to wrap at * @param startColumn the column position of the beginning of the original text - * @param trailing extra space to leave after the last line - * @param components the text to reflow - * @param first0 true if the text includes the beginning of its enclosing concat chain, i.e. a + * @param trailing extra space to leave after the last line, to accommodate a ; or ) + * @param components the text to reflow. This is a list of “words” of a single literal. Its first + * and last quotes have been stripped + * @param first0 true if the text includes the beginning of its enclosing concat chain */ private static String reflow( String separator, @@ -251,7 +252,7 @@ public final class StringWrapper { ImmutableList<String> components, boolean first0) { // We have space between the start column and the limit to output the first line. - // Reserve two spaces for the quotes. + // Reserve two spaces for the start and end quotes. int width = columnLimit - startColumn - 2; Deque<String> input = new ArrayDeque<>(components); List<String> lines = new ArrayList<>(); @@ -259,10 +260,13 @@ public final class StringWrapper { while (!input.isEmpty()) { int length = 0; List<String> line = new ArrayList<>(); - if (input.stream().mapToInt(x -> x.length()).sum() <= width) { + // If we know this is going to be the last line, then remove a bit of width to account for the + // trailing characters. + if (input.stream().mapToInt(String::length).sum() <= width) { + // This isn’t quite optimal, but arguably good enough. See b/179561701 width -= trailing; } - while (!input.isEmpty() && (length <= 4 || (length + input.peekFirst().length()) < width)) { + while (!input.isEmpty() && (length <= 4 || (length + input.peekFirst().length()) <= width)) { String text = input.removeFirst(); line.add(text); length += text.length(); diff --git a/core/src/main/java/com/google/googlejavaformat/java/TypeNameClassifier.java b/core/src/main/java/com/google/googlejavaformat/java/TypeNameClassifier.java index 4e871a6..21fae5f 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/TypeNameClassifier.java +++ b/core/src/main/java/com/google/googlejavaformat/java/TypeNameClassifier.java @@ -164,7 +164,7 @@ public final class TypeNameClassifier { hasLowercase |= Character.isLowerCase(c); } if (firstUppercase) { - return hasLowercase ? UPPER_CAMEL : UPPERCASE; + return (hasLowercase || name.length() == 1) ? UPPER_CAMEL : UPPERCASE; } else { return hasUppercase ? LOWER_CAMEL : LOWERCASE; } diff --git a/core/src/main/java/com/google/googlejavaformat/java/UsageException.java b/core/src/main/java/com/google/googlejavaformat/java/UsageException.java index 82c0843..a10f2d0 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/UsageException.java +++ b/core/src/main/java/com/google/googlejavaformat/java/UsageException.java @@ -46,9 +46,9 @@ final class UsageException extends Exception { " Do not fix the import order. Unused imports will still be removed.", " --skip-removing-unused-imports", " Do not remove unused imports. Imports will still be sorted.", - " . --skip-reflowing-long-strings", + " --skip-reflowing-long-strings", " Do not reflow string literals that exceed the column limit.", - " . --skip-javadoc-formatting", + " --skip-javadoc-formatting", " Do not reformat javadoc.", " --dry-run, -n", " Prints the paths of the files whose contents would change if the formatter were run" 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 index 78cfd66..890687f 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/java14/Java14InputAstVisitor.java @@ -15,48 +15,113 @@ package com.google.googlejavaformat.java.java14; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.MoreCollectors.toOptional; +import static com.google.common.collect.Iterables.getOnlyElement; 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.OpsBuilder.BlankLineWanted; 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.CaseTree; import com.sun.source.tree.ClassTree; -import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.ModuleTree; import com.sun.source.tree.SwitchExpressionTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; 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.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. */ 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 Java14InputAstVisitor(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); + if (module != null) { + if (!first) { + builder.blankLineWanted(BlankLineWanted.YES); + } + markForPartialFormat(); + visitModule(module, null); + builder.forcedBreak(); + } + } + + @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); + } + } + + @Override public Void visitBindingPattern(BindingPatternTree node, Void unused) { sync(node); - scan(node.getType(), null); - builder.breakOp(" "); - visit(node.getBinding()); + 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"); + } return null; } + private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { + if (modifiers != null) { + List<AnnotationTree> annotations = + visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty()); + visitAnnotations(annotations, BreakOrNot.NO, BreakOrNot.YES); + } + scan(type, null); + builder.breakOp(" "); + visit(name); + } + @Override public Void visitYield(YieldTree node, Void aVoid) { sync(node); @@ -98,14 +163,9 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { public void visitRecordDeclaration(ClassTree node) { sync(node); - List<Op> breaks = - visitModifiers( - node.getModifiers(), - Direction.VERTICAL, - /* declarationAnnotationBreak= */ Optional.empty()); + typeDeclarationModifiers(node.getModifiers()); Verify.verify(node.getExtendsClause() == null); boolean hasSuperInterfaceTypes = !node.getImplementsClause().isEmpty(); - builder.addAll(breaks); token("record"); builder.space(); visit(node.getSimpleName()); @@ -117,10 +177,7 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { if (!node.getTypeParameters().isEmpty()) { typeParametersRest(node.getTypeParameters(), hasSuperInterfaceTypes ? plusFour : ZERO); } - ImmutableList<JCVariableDecl> parameters = - compactRecordConstructor(node) - .map(m -> ImmutableList.copyOf(m.getParameters())) - .orElseGet(() -> recordVariables(node)); + ImmutableList<JCVariableDecl> parameters = recordVariables(node); token("("); if (!parameters.isEmpty()) { // Break before args. @@ -159,14 +216,6 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { 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) @@ -199,20 +248,33 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { sync(node); markForPartialFormat(); builder.forcedBreak(); - if (node.getExpressions().isEmpty()) { + 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(); + } + if (isDefault) { token("default", plusTwo); } else { token("case", plusTwo); + builder.open(labels.size() > 1 ? plusFour : ZERO); builder.space(); boolean first = true; - for (ExpressionTree expression : node.getExpressions()) { + for (Tree expression : labels) { if (!first) { token(","); - builder.space(); + builder.breakOp(" "); } scan(expression, null); first = false; } + builder.close(); } switch (node.getCaseKind()) { case STATEMENT: @@ -226,7 +288,16 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { token("-"); token(">"); builder.space(); - scan(node.getBody(), null); + if (node.getBody().getKind() == Tree.Kind.BLOCK) { + // Explicit call with {@link CollapseEmptyOrNot.YES} to handle empty case blocks. + visitBlock( + (BlockTree) node.getBody(), + CollapseEmptyOrNot.YES, + AllowLeadingBlankLine.NO, + AllowTrailingBlankLine.NO); + } else { + scan(node.getBody(), null); + } builder.guessToken(";"); break; default: @@ -234,4 +305,20 @@ public class Java14InputAstVisitor extends JavaInputAstVisitor { } 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); + } + } } diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java index 5addc67..03938a6 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocFormatter.java @@ -166,15 +166,30 @@ public final class JavadocFormatter { * fits on one line. */ private static String makeSingleLineIfPossible(int blockIndent, String input) { - int oneLinerContentLength = MAX_LINE_LENGTH - "/** */".length() - blockIndent; Matcher matcher = ONE_CONTENT_LINE_PATTERN.matcher(input); - if (matcher.matches() && matcher.group(1).isEmpty()) { - return "/** */"; - } else if (matcher.matches() && matcher.group(1).length() <= oneLinerContentLength) { - return "/** " + matcher.group(1) + " */"; + if (matcher.matches()) { + String line = matcher.group(1); + if (line.isEmpty()) { + return "/** */"; + } else if (oneLineJavadoc(line, blockIndent)) { + return "/** " + line + " */"; + } } return input; } + private static boolean oneLineJavadoc(String line, int blockIndent) { + int oneLinerContentLength = MAX_LINE_LENGTH - "/** */".length() - blockIndent; + if (line.length() > oneLinerContentLength) { + return false; + } + // If the javadoc contains only a tag, use multiple lines to encourage writing a summary + // fragment, unless it's /* @hide */. + if (line.startsWith("@") && !line.equals("@hide")) { + return false; + } + return true; + } + private JavadocFormatter() {} } diff --git a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java index c2431c4..0361415 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/javadoc/JavadocWriter.java @@ -15,6 +15,7 @@ package com.google.googlejavaformat.java.javadoc; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Comparators.max; import static com.google.common.collect.Sets.immutableEnumSet; import static com.google.googlejavaformat.java.javadoc.JavadocWriter.AutoIndent.AUTO_INDENT; import static com.google.googlejavaformat.java.javadoc.JavadocWriter.AutoIndent.NO_AUTO_INDENT; @@ -26,9 +27,7 @@ import static com.google.googlejavaformat.java.javadoc.Token.Type.HEADER_OPEN_TA import static com.google.googlejavaformat.java.javadoc.Token.Type.LIST_ITEM_OPEN_TAG; import static com.google.googlejavaformat.java.javadoc.Token.Type.PARAGRAPH_OPEN_TAG; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Ordering; import com.google.googlejavaformat.java.javadoc.Token.Type; /** @@ -270,8 +269,7 @@ final class JavadocWriter { } private void requestWhitespace(RequestedWhitespace requestedWhitespace) { - this.requestedWhitespace = - Ordering.natural().max(requestedWhitespace, this.requestedWhitespace); + this.requestedWhitespace = max(requestedWhitespace, this.requestedWhitespace); } /** @@ -396,7 +394,7 @@ final class JavadocWriter { // If this is a hotspot, keep a String of many spaces around, and call append(string, start, end). private void appendSpaces(int count) { - output.append(Strings.repeat(" ", count)); + output.append(" ".repeat(count)); } /** 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 new file mode 100644 index 0000000..2c65803 --- /dev/null +++ b/core/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,6 @@ +[ + { + "name": "com.sun.tools.javac.parser.UnicodeReader", + "allDeclaredMethods": true + } +] diff --git a/core/src/main/scripts/google-java-format.el b/core/src/main/scripts/google-java-format.el index f9e8d2a..f269ab3 100644 --- a/core/src/main/scripts/google-java-format.el +++ b/core/src/main/scripts/google-java-format.el @@ -46,7 +46,7 @@ A string containing the name or the full path of the executable." :group 'google-java-format - :type '(file :must-match t :match #'file-executable-p) + :type '(file :must-match t :match (lambda (widget file) (file-executable-p file))) :risky t) ;;;###autoload diff --git a/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java b/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java index 8d71f4d..1a4ed09 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/CommandLineOptionsParserTest.java @@ -19,12 +19,12 @@ import static com.google.common.truth.Truth8.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Range; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; -import java.util.Collections; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -39,7 +39,7 @@ public class CommandLineOptionsParserTest { @Test public void defaults() { - CommandLineOptions options = CommandLineOptionsParser.parse(Collections.<String>emptyList()); + CommandLineOptions options = CommandLineOptionsParser.parse(ImmutableList.of()); assertThat(options.files()).isEmpty(); assertThat(options.stdin()).isFalse(); assertThat(options.aosp()).isFalse(); @@ -159,7 +159,7 @@ public class CommandLineOptionsParserTest { CommandLineOptionsParser.parse(Arrays.asList("-lines=1:1", "-lines=1:1")); fail(); } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains("overlap"); + assertThat(e).hasMessageThat().contains("overlap"); } } 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 0b81ba6..fc966fa 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/DiagnosticTest.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; @@ -98,8 +97,7 @@ public class DiagnosticTest { int result = main.format(path.toString()); assertThat(stdout.toString()).isEmpty(); - assertThat(stderr.toString()) - .contains("InvalidSyntax.java:1:35: error: illegal unicode escape"); + assertThat(stderr.toString()).contains("error: illegal unicode escape"); assertThat(result).isEqualTo(1); } @@ -156,7 +154,7 @@ public class DiagnosticTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("A.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -173,7 +171,7 @@ public class DiagnosticTest { public void parseErrorStdin() throws FormatterException, IOException, UsageException { String input = "class Foo { void f() {\n g() } }"; - InputStream inStream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); + InputStream inStream = new ByteArrayInputStream(input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), inStream); @@ -190,7 +188,7 @@ public class DiagnosticTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("A.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -206,7 +204,7 @@ public class DiagnosticTest { @Test public void lexErrorStdin() throws FormatterException, IOException, UsageException { String input = "class Foo { void f() {\n g('foo'); } }"; - InputStream inStream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); + InputStream inStream = new ByteArrayInputStream(input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), inStream); 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 44ba639..61a4346 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java @@ -14,8 +14,7 @@ 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.collect.MoreCollectors.toOptional; import static com.google.common.io.Files.getFileExtension; import static com.google.common.io.Files.getNameWithoutExtension; import static java.nio.charset.StandardCharsets.UTF_8; @@ -23,7 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableMultimap; import com.google.common.io.CharStreams; import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath.ResourceInfo; @@ -31,12 +30,12 @@ import com.google.googlejavaformat.Newlines; 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; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.TreeMap; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,8 +46,13 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class FormatterIntegrationTest { - private static final ImmutableSet<String> JAVA14_TESTS = - ImmutableSet.of("I477", "Records", "RSLs", "Var", "ExpressionSwitch"); + private static final ImmutableMultimap<Integer, String> VERSIONED_TESTS = + ImmutableMultimap.<Integer, String>builder() + .putAll(14, "I477", "Records", "RSLs", "Var", "ExpressionSwitch", "I574", "I594") + .putAll(15, "I603") + .putAll(16, "I588") + .putAll(17, "I683", "I684", "I696") + .build(); @Parameters(name = "{index}: {0}") public static Iterable<Object[]> data() throws IOException { @@ -76,7 +80,7 @@ public class FormatterIntegrationTest { case "output": outputs.put(baseName, contents); break; - default: + default: // fall out } } } @@ -87,7 +91,9 @@ public class FormatterIntegrationTest { String input = inputs.get(fileName); assertTrue("unmatched input", outputs.containsKey(fileName)); String expectedOutput = outputs.get(fileName); - if (JAVA14_TESTS.contains(fileName) && getMajor() < 14) { + Optional<Integer> version = + VERSIONED_TESTS.inverse().get(fileName).stream().collect(toOptional()); + if (version.isPresent() && Runtime.version().feature() < version.get()) { continue; } testInputs.add(new Object[] {fileName, input, expectedOutput}); @@ -95,21 +101,6 @@ public class FormatterIntegrationTest { return testInputs; } - 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()); - } - private final String name; private final String input; private final String expected; @@ -125,7 +116,9 @@ public class FormatterIntegrationTest { @Test public void format() { try { - String output = new Formatter().formatSource(input); + Formatter formatter = new Formatter(); + String output = formatter.formatSource(input); + output = StringWrapper.wrap(output, formatter); assertEquals("bad output for " + name, expected, output); } catch (FormatterException e) { fail(String.format("Formatter crashed on %s: %s", name, e.getMessage())); diff --git a/core/src/test/java/com/google/googlejavaformat/java/FormatterTest.java b/core/src/test/java/com/google/googlejavaformat/java/FormatterTest.java index 3f6e974..1653e56 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterTest.java @@ -16,6 +16,7 @@ package com.google.googlejavaformat.java; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; import com.google.common.base.Joiner; @@ -27,7 +28,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import org.junit.Rule; @@ -63,7 +63,7 @@ public final class FormatterTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("A.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -94,7 +94,7 @@ public final class FormatterTest { String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n"; String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; - InputStream in = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); + InputStream in = new ByteArrayInputStream(input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -115,7 +115,7 @@ public final class FormatterTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -132,7 +132,7 @@ public final class FormatterTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -238,7 +238,7 @@ public final class FormatterTest { "", "import java.util.List;", "", - "import javax.annotations.Nullable;"); + "import javax.annotation.Nullable;"); @Test public void importsNotReorderedByDefault() throws FormatterException { @@ -262,7 +262,7 @@ public final class FormatterTest { String expect = "package com.google.example;\n\n" + "import java.util.List;\n" - + "import javax.annotations.Nullable;\n\n" + + "import javax.annotation.Nullable;\n\n" + "public class ExampleTest {\n" + " @Nullable List<?> xs;\n" + "}\n"; @@ -302,7 +302,7 @@ public final class FormatterTest { String inputResourceName = "com/google/googlejavaformat/java/testimports/A.input"; String input = getResource(inputResourceName); String expectedOutput = getResource(outputResourceName); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -315,14 +315,14 @@ public final class FormatterTest { assertThat(err.toString()).isEmpty(); assertThat(out.toString()).isEmpty(); - String output = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + String output = new String(Files.readAllBytes(path), UTF_8); assertThat(output).isEqualTo(expectedOutput); } private String getResource(String resourceName) throws IOException { try (InputStream stream = getClass().getClassLoader().getResourceAsStream(resourceName)) { assertWithMessage("Missing resource: " + resourceName).that(stream).isNotNull(); - return CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8)); + return CharStreams.toString(new InputStreamReader(stream, UTF_8)); } } diff --git a/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java new file mode 100644 index 0000000..15e4522 --- /dev/null +++ b/core/src/test/java/com/google/googlejavaformat/java/GoogleJavaFormatToolProviderTest.java @@ -0,0 +1,57 @@ +/* + * 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.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ServiceLoader; +import java.util.spi.ToolProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link GoogleJavaFormatToolProvider}. */ +@RunWith(JUnit4.class) +public class GoogleJavaFormatToolProviderTest { + + @Test + public void testUsageOutputAfterLoadingViaToolName() { + String name = "google-java-format"; + + assertThat( + ServiceLoader.load(ToolProvider.class).stream() + .map(ServiceLoader.Provider::get) + .map(ToolProvider::name)) + .contains(name); + + ToolProvider format = ToolProvider.findFirst(name).get(); + + StringWriter out = new StringWriter(); + StringWriter err = new StringWriter(); + + int result = format.run(new PrintWriter(out, true), new PrintWriter(err, true), "--help"); + + assertThat(result).isEqualTo(0); + + String usage = err.toString(); + + // Check that doc links are included. + assertThat(usage).containsMatch("http.*/google-java-format"); + assertThat(usage).contains("Usage: google-java-format"); + } +} diff --git a/core/src/test/java/com/google/googlejavaformat/java/ImportOrdererTest.java b/core/src/test/java/com/google/googlejavaformat/java/ImportOrdererTest.java index 5a6b1f9..0b9dab2 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/ImportOrdererTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/ImportOrdererTest.java @@ -313,7 +313,7 @@ public class ImportOrdererTest { "", "import java.util.List;", "", - "import javax.annotations.Nullable;", + "import javax.annotation.Nullable;", "", "import static org.junit.Assert.fail;", "import static com.google.truth.Truth.assertThat;", @@ -329,7 +329,7 @@ public class ImportOrdererTest { "", "import com.google.common.base.Preconditions;", "import java.util.List;", - "import javax.annotations.Nullable;", + "import javax.annotation.Nullable;", "import org.junit.runner.RunWith;", "import org.junit.runners.JUnit4;", "", @@ -527,6 +527,27 @@ public class ImportOrdererTest { "class Test {}", } }, + { + { + "package p;", + "", + "import java.lang.Bar;", + "import java.lang.Baz;", + ";", + "import java.lang.Foo;", + "", + "interface Test {}", + }, + { + "package p;", + "", + "import java.lang.Bar;", + "import java.lang.Baz;", + "import java.lang.Foo;", + "", + "interface Test {}", + } + } }; ImmutableList.Builder<Object[]> builder = ImmutableList.builder(); @@ -799,7 +820,7 @@ public class ImportOrdererTest { "", "public class Blim {}", }, - }, + } }; ImmutableList.Builder<Object[]> builder = ImmutableList.builder(); Arrays.stream(inputsOutputs).forEach(input -> builder.add(createRow(input))); diff --git a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java index f5103d9..6849c01 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/JavadocFormattingTest.java @@ -937,7 +937,9 @@ public final class JavadocFormattingTest { "class Test {}", }; String[] expected = { - "/** @param this is a param */", // + "/**", // + " * @param this is a param", + " */", "class Test {}", }; doFormatTest(input, expected); @@ -1415,4 +1417,33 @@ public final class JavadocFormattingTest { }; doFormatTest(input, expected); } + + @Test + public void missingSummaryFragment() { + String[] input = { + "public class Foo {", + " /**", + " * @return something.", + " */", + " public void setSomething() {}", + "", + " /**", + " * @hide", + " */", + " public void setSomething() {}", + "}", + }; + String[] expected = { + "public class Foo {", + " /**", + " * @return something.", + " */", + " public void setSomething() {}", + "", + " /** @hide */", + " public void setSomething() {}", + "}", + }; + doFormatTest(input, expected); + } } diff --git a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java index 613d391..ac3eb39 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/MainTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/MainTest.java @@ -14,6 +14,8 @@ package com.google.googlejavaformat.java; +import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; +import static com.google.common.base.StandardSystemProperty.JAVA_HOME; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.nio.charset.StandardCharsets.UTF_8; @@ -50,6 +52,16 @@ public class MainTest { // PrintWriter instances used below are hard-coded to use system-default line separator. private final Joiner joiner = Joiner.on(System.lineSeparator()); + private static final ImmutableList<String> ADD_EXPORTS = + ImmutableList.of( + "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"); + @Test public void testUsageOutput() { StringWriter out = new StringWriter(); @@ -107,11 +119,13 @@ public class MainTest { public void testMain() throws Exception { Process process = new ProcessBuilder( - ImmutableList.of( - Paths.get(System.getProperty("java.home")).resolve("bin/java").toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName())) + ImmutableList.<String>builder() + .add(Paths.get(JAVA_HOME.value()).resolve("bin/java").toString()) + .addAll(ADD_EXPORTS) + .add("-cp") + .add(JAVA_CLASS_PATH.value()) + .add(Main.class.getName()) + .build()) .redirectError(Redirect.PIPE) .redirectOutput(Redirect.PIPE) .start(); @@ -308,7 +322,7 @@ public class MainTest { "@ParametersAreNonnullByDefault", "package com.google.common.labs.base;", "", - "import javax.annotation.CheckReturnValue;", + "import com.google.errorprone.annotations.CheckReturnValue;", "import javax.annotation.ParametersAreNonnullByDefault;", "", }; @@ -388,9 +402,9 @@ public class MainTest { assertThat(out.toString()) .isEqualTo( - b.toAbsolutePath().toString() + b.toAbsolutePath() + System.lineSeparator() - + c.toAbsolutePath().toString() + + c.toAbsolutePath() + System.lineSeparator()); assertThat(err.toString()).isEmpty(); } @@ -433,14 +447,16 @@ public class MainTest { Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); Process process = new ProcessBuilder( - ImmutableList.of( - Paths.get(System.getProperty("java.home")).resolve("bin/java").toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName(), - "-n", - "--set-exit-if-changed", - "-")) + ImmutableList.<String>builder() + .add(Paths.get(JAVA_HOME.value()).resolve("bin/java").toString()) + .addAll(ADD_EXPORTS) + .add("-cp") + .add(JAVA_CLASS_PATH.value()) + .add(Main.class.getName()) + .add("-n") + .add("--set-exit-if-changed") + .add("-") + .build()) .redirectInput(path.toFile()) .redirectError(Redirect.PIPE) .redirectOutput(Redirect.PIPE) @@ -459,14 +475,16 @@ public class MainTest { Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); Process process = new ProcessBuilder( - ImmutableList.of( - Paths.get(System.getProperty("java.home")).resolve("bin/java").toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName(), - "-n", - "--set-exit-if-changed", - path.toAbsolutePath().toString())) + ImmutableList.<String>builder() + .add(Paths.get(JAVA_HOME.value()).resolve("bin/java").toString()) + .addAll(ADD_EXPORTS) + .add("-cp") + .add(JAVA_CLASS_PATH.value()) + .add(Main.class.getName()) + .add("-n") + .add("--set-exit-if-changed") + .add(path.toAbsolutePath().toString()) + .build()) .redirectError(Redirect.PIPE) .redirectOutput(Redirect.PIPE) .start(); @@ -474,7 +492,7 @@ public class MainTest { String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); assertThat(err).isEmpty(); - assertThat(out).isEqualTo(path.toAbsolutePath().toString() + System.lineSeparator()); + assertThat(out).isEqualTo(path.toAbsolutePath() + System.lineSeparator()); assertThat(process.exitValue()).isEqualTo(1); } @@ -523,8 +541,8 @@ public class MainTest { "class T {", " String s =", " \"one long incredibly unbroken sentence moving from topic to topic so that no one had" - + " a\"", - " + \" chance to interrupt\";", + + " a chance\"", + " + \" to interrupt\";", "}", "", }; diff --git a/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java b/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java index 57d55d7..b1142b3 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/PartialFormattingTest.java @@ -25,7 +25,6 @@ import com.google.common.collect.Range; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -397,7 +396,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -429,7 +428,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -475,7 +474,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -521,7 +520,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -567,7 +566,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -714,7 +713,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("FormatterException.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -957,7 +956,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1081,7 +1080,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1108,7 +1107,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1133,7 +1132,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1156,7 +1155,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1177,7 +1176,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1429,7 +1428,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, lines(input).getBytes(StandardCharsets.UTF_8)); + Files.write(path, lines(input).getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1459,7 +1458,7 @@ public final class PartialFormattingTest { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, lines(input).getBytes(StandardCharsets.UTF_8)); + Files.write(path, lines(input).getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1505,7 +1504,7 @@ public final class PartialFormattingTest { private String formatMain(String input, String... args) throws Exception { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Test.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); @@ -1655,7 +1654,7 @@ public final class PartialFormattingTest { private String runFormatter(String input, String[] args) throws IOException, UsageException { Path tmpdir = testFolder.newFolder().toPath(); Path path = tmpdir.resolve("Foo.java"); - Files.write(path, input.getBytes(StandardCharsets.UTF_8)); + Files.write(path, input.getBytes(UTF_8)); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); diff --git a/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsCaseLabelsTest.java b/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsCaseLabelsTest.java new file mode 100644 index 0000000..c0babb0 --- /dev/null +++ b/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsCaseLabelsTest.java @@ -0,0 +1,49 @@ +/* + * 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.truth.Truth.assertThat; +import static com.google.googlejavaformat.java.RemoveUnusedImports.removeUnusedImports; +import static org.junit.Assume.assumeTrue; + +import com.google.common.base.Joiner; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests that unused import removal doesn't remove types used in case labels. */ +@RunWith(JUnit4.class) +public class RemoveUnusedImportsCaseLabelsTest { + @Test + public void preserveTypesInCaseLabels() throws FormatterException { + assumeTrue(Runtime.version().feature() >= 17); + String input = + Joiner.on('\n') + .join( + "package example;", + "import example.model.SealedInterface;", + "import example.model.TypeA;", + "import example.model.TypeB;", + "public class Main {", + " public void apply(SealedInterface sealedInterface) {", + " switch(sealedInterface) {", + " case TypeA a -> System.out.println(\"A!\");", + " case TypeB b -> System.out.println(\"B!\");", + " }", + " }", + "}"); + assertThat(removeUnusedImports(input)).isEqualTo(input); + } +} diff --git a/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsTest.java b/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsTest.java index 1965feb..675bc88 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/RemoveUnusedImportsTest.java @@ -258,7 +258,7 @@ public class RemoveUnusedImportsTest { }; ImmutableList.Builder<Object[]> builder = ImmutableList.builder(); for (String[][] inputAndOutput : inputsOutputs) { - assertThat(inputAndOutput.length).isEqualTo(2); + assertThat(inputAndOutput).hasLength(2); String[] input = inputAndOutput[0]; String[] output = inputAndOutput[1]; String[] parameters = { diff --git a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperIntegrationTest.java b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperIntegrationTest.java index 89c94ea..53fb54d 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/StringWrapperIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/StringWrapperIntegrationTest.java @@ -395,8 +395,8 @@ public class StringWrapperIntegrationTest { @Test public void testCR() throws Exception { - assertThat(StringWrapper.wrap(40, formatter.formatSource(input.replace("\n", "\r")), formatter)) - .isEqualTo(output.replace("\n", "\r")); + assertThat(StringWrapper.wrap(40, formatter.formatSource(input.replace('\n', '\r')), formatter)) + .isEqualTo(output.replace('\n', '\r')); } @Test diff --git a/core/src/test/java/com/google/googlejavaformat/java/TypeNameClassifierTest.java b/core/src/test/java/com/google/googlejavaformat/java/TypeNameClassifierTest.java index 9d1e00a..3270bc6 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/TypeNameClassifierTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/TypeNameClassifierTest.java @@ -43,6 +43,7 @@ public final class TypeNameClassifierTest { assertThat(JavaCaseFormat.from("a_$")).isEqualTo(JavaCaseFormat.LOWERCASE); assertThat(JavaCaseFormat.from("_")).isEqualTo(JavaCaseFormat.LOWERCASE); assertThat(JavaCaseFormat.from("_A")).isEqualTo(JavaCaseFormat.UPPERCASE); + assertThat(JavaCaseFormat.from("A")).isEqualTo(JavaCaseFormat.UPPER_CAMEL); } private static Optional<Integer> getPrefix(String qualifiedName) { @@ -62,6 +63,7 @@ public final class TypeNameClassifierTest { assertThat(getPrefix("ClassName.CONST")).hasValue(1); assertThat(getPrefix("ClassName.varName")).hasValue(1); assertThat(getPrefix("ClassName.Inner.varName")).hasValue(2); + assertThat(getPrefix("com.R.foo")).hasValue(2); } @Test diff --git a/core/src/test/java/com/google/googlejavaformat/java/filer/FormattingFilerTest.java b/core/src/test/java/com/google/googlejavaformat/java/filer/FormattingFilerTest.java index 4fef207..38cac35 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/filer/FormattingFilerTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/filer/FormattingFilerTest.java @@ -52,7 +52,7 @@ public class FormattingFilerTest { new Messager() { @Override public void printMessage(javax.tools.Diagnostic.Kind kind, CharSequence msg) { - logMessages.add(kind.toString() + ";" + msg); + logMessages.add(kind + ";" + msg); } @Override @@ -73,9 +73,9 @@ public class FormattingFilerTest { String file = Joiner.on('\n').join("package foo;", "public class Bar {"); FormattingFiler formattingFiler = new FormattingFiler(new FakeFiler(), messager); - Writer writer = formattingFiler.createSourceFile("foo.Bar").openWriter(); - writer.write(file); - writer.close(); + try (Writer writer = formattingFiler.createSourceFile("foo.Bar").openWriter()) { + writer.write(file); + } assertThat(logMessages).containsExactly("NOTE;Error formatting foo.Bar"); } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.input index 81d13aa..c658630 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.input @@ -17,13 +17,13 @@ class A { @X(x = 1) private @interface Y {} - // TODO(jdd): Add annotation declaration with empty body. + // TODO(user): Add annotation declaration with empty body. @X(x = 1) @Y protected @interface Z {} - // TODO(jdd): Include type annotations once we can include a higher language level. + // TODO(user): Include type annotations once we can include a higher language level. int[] array1 = new int[5]; int[] array2 = diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.output index 3eff456..5d5d88f 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/A.output @@ -17,13 +17,13 @@ class A { @X(x = 1) private @interface Y {} - // TODO(jdd): Add annotation declaration with empty body. + // TODO(user): Add annotation declaration with empty body. @X(x = 1) @Y protected @interface Z {} - // TODO(jdd): Include type annotations once we can include a higher language level. + // TODO(user): Include type annotations once we can include a higher language level. int[] array1 = new int[5]; int[] array2 = diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.input new file mode 100644 index 0000000..e3e8493 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.input @@ -0,0 +1,8 @@ +class B173808510 { + // b/173808510 + @FlagSpec( + name = "myFlag", + help = + "areallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyloongword word1 word2") + Flag<Integer> dummy = null; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.output new file mode 100644 index 0000000..45a939e --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B173808510.output @@ -0,0 +1,9 @@ +class B173808510 { + // b/173808510 + @FlagSpec( + name = "myFlag", + help = + "areallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyloongword word1" + + " word2") + Flag<Integer> dummy = null; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.input new file mode 100644 index 0000000..7c220d5 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.input @@ -0,0 +1,4 @@ +class B183431894 { + int a = - -1; + int d = + +1; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.output new file mode 100644 index 0000000..7c220d5 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B183431894.output @@ -0,0 +1,4 @@ +class B183431894 { + int a = - -1; + int d = + +1; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.input index 30c232b..9408235 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.input @@ -5,7 +5,7 @@ class Test { if (!metadata.ignoreOutputTransformations() && Producers.isListenableFutureMapKey(outputKey)) { ImmutableList<ProducerNode<?>> nodes = createMapNodes((ProducerNode) node); - checkCollectionNodesAgainstWhitelist(nodes, whitelist); + checkCollectionNodesAgainstAllowlist(nodes, allowlist); return nodes; } else if (!metadata.ignoreOutputTransformations() diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.output index 950f4eb..aeb36b8 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20535125.output @@ -4,7 +4,7 @@ class Test { void m() { if (!metadata.ignoreOutputTransformations() && Producers.isListenableFutureMapKey(outputKey)) { ImmutableList<ProducerNode<?>> nodes = createMapNodes((ProducerNode) node); - checkCollectionNodesAgainstWhitelist(nodes, whitelist); + checkCollectionNodesAgainstAllowlist(nodes, allowlist); return nodes; } else if (!metadata.ignoreOutputTransformations() diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.input index 957c2df..de746bb 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.input @@ -1,8 +1,8 @@ class B20701054 { void m() { ImmutableList<String> x = ImmutableList.builder().add(1).build(); - OptionalBinder.<ASD>newOptionalBinder(binder(), InputWhitelist.class).setBinding().to( - AllInputWhitelist.class); + OptionalBinder.<ASD>newOptionalBinder(binder(), InputAllowlist.class).setBinding().to( + AllInputAllowlist.class); Foo z = Foo.INSTANCE.field; Foo z = Foo.INSTANCE.field.field; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.output index 7ce6fda..2fd9a9a 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20701054.output @@ -1,9 +1,9 @@ class B20701054 { void m() { ImmutableList<String> x = ImmutableList.builder().add(1).build(); - OptionalBinder.<ASD>newOptionalBinder(binder(), InputWhitelist.class) + OptionalBinder.<ASD>newOptionalBinder(binder(), InputAllowlist.class) .setBinding() - .to(AllInputWhitelist.class); + .to(AllInputAllowlist.class); Foo z = Foo.INSTANCE.field; Foo z = Foo.INSTANCE.field.field; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input index 86e46d5..7317f17 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.input @@ -1,6 +1,6 @@ public class B20844369 { private static final String ID_PATTERN = - // TODO(daw): add min/max lengths for the numbers here, e.g. android ID + // TODO(user): add min/max lengths for the numbers here, e.g. android ID "(?:(?<androidId>\\d+)\\+)?" // optional Android ID + "(?<type>\\d+)" // type + ":" diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output index 982dc2b..62f9721 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/B20844369.output @@ -1,6 +1,6 @@ public class B20844369 { private static final String ID_PATTERN = - // TODO(daw): add min/max lengths for the numbers here, e.g. android ID + // TODO(user): add min/max lengths for the numbers here, e.g. android ID "(?:(?<androidId>\\d+)\\+)?" // optional Android ID + "(?<type>\\d+)" // type + ":" diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.input index 7baed6c..31bf3b8 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.input @@ -6,7 +6,7 @@ package com.google.googlejavaformat.java.test; * CreationReferences. */ class C<T> { - // TODO(jdd): Test higher-language-level constructs. + // TODO(user): Test higher-language-level constructs. C() { this( diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.output index fcf773e..c62c7ae 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/C.output @@ -6,7 +6,7 @@ package com.google.googlejavaformat.java.test; * CreationReferences. */ class C<T> { - // TODO(jdd): Test higher-language-level constructs. + // TODO(user): Test higher-language-level constructs. C() { this( diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.input index daca973..d69ed1e 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.input @@ -2,7 +2,7 @@ package com.google.googlejavaformat.java.test; /** Tests for Dimensions and DoStatements. */ class D { - // TODO(jdd): Test higher-language-level features. + // TODO(user): Test higher-language-level features. int[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [][][][] diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.output index daca973..d69ed1e 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/D.output @@ -2,7 +2,7 @@ package com.google.googlejavaformat.java.test; /** Tests for Dimensions and DoStatements. */ class D { - // TODO(jdd): Test higher-language-level features. + // TODO(user): Test higher-language-level features. int[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][] [][][][] diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.input index 0e98139..479466a 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.input @@ -9,7 +9,7 @@ import com.google.common.collect.Lists; */ @MarkerAnnotation class E<T> { - // TODO(jdd): Test higher language-level features. + // TODO(user): Test higher language-level features. enum Enum1 { A, B, C, D; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.output index 4dd603a..fb4f2fa 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/E.output @@ -9,7 +9,7 @@ import com.google.common.collect.Lists; */ @MarkerAnnotation class E<T> { - // TODO(jdd): Test higher language-level features. + // TODO(user): Test higher language-level features. enum Enum1 { A, diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.input index 1e4db16..5590f74 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.input @@ -25,4 +25,17 @@ class ExpressionSwitch { default -> System.out.println("default"); } } + + String breakLongCaseArgs(MyEnum e) { + return switch (e) { + case SOME_RATHER_LONG_NAME_1, SOME_RATHER_LONG_NAME_2, SOME_RATHER_LONG_NAME_3, SOME_RATHER_LONG_NAME_4, SOME_RATHER_LONG_NAME_5, SOME_RATHER_LONG_NAME_6, SOME_RATHER_LONG_NAME_7 -> {} + case SOME_RATHER_LONG_NAME_8 -> {} + }; + } + + String dontBreakShortCaseArgs(MyEnum e) { + return switch (e) { + case CASE_A, CASE_B -> {} + }; + } } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.output index 6458aa0..00ae892 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/ExpressionSwitch.output @@ -31,4 +31,23 @@ class ExpressionSwitch { default -> System.out.println("default"); } } + + String breakLongCaseArgs(MyEnum e) { + return switch (e) { + case SOME_RATHER_LONG_NAME_1, + SOME_RATHER_LONG_NAME_2, + SOME_RATHER_LONG_NAME_3, + SOME_RATHER_LONG_NAME_4, + SOME_RATHER_LONG_NAME_5, + SOME_RATHER_LONG_NAME_6, + SOME_RATHER_LONG_NAME_7 -> {} + case SOME_RATHER_LONG_NAME_8 -> {} + }; + } + + String dontBreakShortCaseArgs(MyEnum e) { + return switch (e) { + case CASE_A, CASE_B -> {} + }; + } } diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.input new file mode 100644 index 0000000..27d23d0 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.input @@ -0,0 +1,6 @@ +public record Record(@NotNull Object o) { + + public Record { + this.o = o; + } +}
\ No newline at end of file diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.output new file mode 100644 index 0000000..b0deb2d --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I574.output @@ -0,0 +1,6 @@ +public record Record(@NotNull Object o) { + + public Record { + this.o = o; + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.input new file mode 100644 index 0000000..9c8f992 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.input @@ -0,0 +1,8 @@ +class T { + int f(Object x) { + if (x instanceof final Integer i) { + return i; + } + return -1; + } +}
\ No newline at end of file diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.output new file mode 100644 index 0000000..37ff2f5 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I588.output @@ -0,0 +1,8 @@ +class T { + int f(Object x) { + if (x instanceof final Integer i) { + return i; + } + return -1; + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.input new file mode 100644 index 0000000..98f667e --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.input @@ -0,0 +1,7 @@ +public class I594 { + public void thisIsNotFormattedCorrectly(Object something){ + if(something instanceof String somethingAsString){ + return; + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.output new file mode 100644 index 0000000..7c519a2 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I594.output @@ -0,0 +1,7 @@ +public class I594 { + public void thisIsNotFormattedCorrectly(Object something) { + if (something instanceof String somethingAsString) { + return; + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.input new file mode 100644 index 0000000..6cedc53 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.input @@ -0,0 +1,16 @@ +class I603 { + sealed abstract class T1 {} + + sealed class T2 extends X implements Y permits Z {} + + sealed class T3 + permits + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {} + + sealed class T4 + implements + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + permits + Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {} +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.output new file mode 100644 index 0000000..22153b6 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I603.output @@ -0,0 +1,13 @@ +class I603 { + abstract sealed class T1 {} + + sealed class T2 extends X implements Y permits Z {} + + sealed class T3 + permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {} + + sealed class T4 + implements Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + permits Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy {} +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.input new file mode 100644 index 0000000..a31a230 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.input @@ -0,0 +1,14 @@ +public class Foo { + static final int VERBOSE_WORDY_AND_LENGTHY_ONE = 1; + static final int VERBOSE_WORDY_AND_LENGTHY_TWO = 2; + static final int VERBOSE_WORDY_AND_LENGTHY_FOUR = 4; + + public static int fn(int x) { + switch (x) { + case VERBOSE_WORDY_AND_LENGTHY_ONE | VERBOSE_WORDY_AND_LENGTHY_TWO | VERBOSE_WORDY_AND_LENGTHY_FOUR: + return 0; + default: + return 1; + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.output new file mode 100644 index 0000000..945cbea --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I643.output @@ -0,0 +1,16 @@ +public class Foo { + static final int VERBOSE_WORDY_AND_LENGTHY_ONE = 1; + static final int VERBOSE_WORDY_AND_LENGTHY_TWO = 2; + static final int VERBOSE_WORDY_AND_LENGTHY_FOUR = 4; + + public static int fn(int x) { + switch (x) { + case VERBOSE_WORDY_AND_LENGTHY_ONE + | VERBOSE_WORDY_AND_LENGTHY_TWO + | VERBOSE_WORDY_AND_LENGTHY_FOUR: + return 0; + default: + return 1; + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.input new file mode 100644 index 0000000..9104f19 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.input @@ -0,0 +1,14 @@ +interface Test { + + static class Test1 implements Test{} + static class Test2 implements Test{} + + public static void main(String[] args) { + Test test = new Test1(); + switch (test) { + case Test1 test1 -> {} + case Test2 test2 -> {} + default -> throw new IllegalStateException("Unexpected value: " + test); + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.output new file mode 100644 index 0000000..5b9c466 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I683.output @@ -0,0 +1,15 @@ +interface Test { + + static class Test1 implements Test {} + + static class Test2 implements Test {} + + public static void main(String[] args) { + Test test = new Test1(); + switch (test) { + case Test1 test1 -> {} + case Test2 test2 -> {} + default -> throw new IllegalStateException("Unexpected value: " + test); + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.input new file mode 100644 index 0000000..cbce0dd --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.input @@ -0,0 +1,14 @@ +package example; + +import example.model.SealedInterface; +import example.model.TypeA; +import example.model.TypeB; + +public class Main { + public void apply(SealedInterface sealedInterface) { + switch(sealedInterface) { + case TypeA a -> System.out.println("A!"); + case TypeB b -> System.out.println("B!"); + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.output new file mode 100644 index 0000000..4e5e9b4 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I684.output @@ -0,0 +1,14 @@ +package example; + +import example.model.SealedInterface; +import example.model.TypeA; +import example.model.TypeB; + +public class Main { + public void apply(SealedInterface sealedInterface) { + switch (sealedInterface) { + case TypeA a -> System.out.println("A!"); + case TypeB b -> System.out.println("B!"); + } + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.input new file mode 100644 index 0000000..156e6ef --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.input @@ -0,0 +1,11 @@ +public abstract non-sealed class A extends SealedClass { +} + +non-sealed class B extends SealedClass { +} + +non-sealed @A class B extends SealedClass { +} + +@A non-sealed class B extends SealedClass { +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.output new file mode 100644 index 0000000..14721c3 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/I696.output @@ -0,0 +1,8 @@ +public abstract non-sealed class A extends SealedClass {} + +non-sealed class B extends SealedClass {} + +non-sealed @A class B extends SealedClass {} + +@A +non-sealed class B extends SealedClass {} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.input index f0b3e66..eda543d 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.input @@ -2,7 +2,7 @@ package com.google.googlejavaformat.java.test; /** Tests for LabeledStatements and LambdaExpressions. */ class L { - // TODO(jdd): Include high language-level tests. + // TODO(user): Include high language-level tests. void f() { LABEL: diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.output index f0b3e66..eda543d 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/L.output @@ -2,7 +2,7 @@ package com.google.googlejavaformat.java.test; /** Tests for LabeledStatements and LambdaExpressions. */ class L { - // TODO(jdd): Include high language-level tests. + // TODO(user): Include high language-level tests. void f() { LABEL: diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.input new file mode 100644 index 0000000..2aa4de3 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.input @@ -0,0 +1,44 @@ +class LiteralReflow { + static class TestLineBreak { + String doesNotBreakAt100 = "A very long long long long long long long long long loong sentence"; + String breaksAt101 = "A very long long long long long long long long long long loooong sentence"; + } + + static class TestReflowLimit { + String doesNotReflowAt100 = + "A very long long long long long long long long long long long long long looooong sentence"; + String reflowsWhenLongerThan100 = + "A very long long long long long long long long long long long long long long long sentence"; + } + + static class TestReflowLocation { + String accommodatesWordsUpTo100 = + "A very long long long long long long long long long long long long long long long looooong sentence"; + String breaksBeforeWordsReach101 = + "A very long long long long long long long long long long long long long long long loooooong sentence"; + } + + static class Test2LineReflowLimit { + String doesNotReflowEitherLinesAt100 = + "A very long long long long long long long long long long long long long looooong sentence. And a second very long long long long long long long long long long loong sentence"; + String reflowsLastLineAt101 = + "A very long long long long long long long long long long long long long looooong sentence. And a second very long long long long long long long long long long looong sentence"; + } + + static class TestWithTrailingCharacters { + String fitsLastLineUpTo100WithTrailingCharacters = + f( + f( + "A very long long long long long long long long long long long long loong sentence. And a second very long long long long long long long long loong sentence")); + String reflowsLastLineToAccommodateTrailingCharacters = + f( + f( + "A very long long long long long long long long long long long long loong sentence. And a second very long long long long long long long long looong sentence")); + // Tests an off-by-one issue, but see b/179561701 for a similar issue that is not yet fixed + String doesNotOverTriggerLastLineReflow = + f( + f( + "A very long long long long long long long long long long long long loong sentence." + + " And a second very loong sentence with trailing a a a a a a a a a a a a a a a")); + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.output new file mode 100644 index 0000000..50ed7bd --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/LiteralReflow.output @@ -0,0 +1,55 @@ +class LiteralReflow { + static class TestLineBreak { + String doesNotBreakAt100 = "A very long long long long long long long long long loong sentence"; + String breaksAt101 = + "A very long long long long long long long long long long loooong sentence"; + } + + static class TestReflowLimit { + String doesNotReflowAt100 = + "A very long long long long long long long long long long long long long looooong sentence"; + String reflowsWhenLongerThan100 = + "A very long long long long long long long long long long long long long long long" + + " sentence"; + } + + static class TestReflowLocation { + String accommodatesWordsUpTo100 = + "A very long long long long long long long long long long long long long long long looooong" + + " sentence"; + String breaksBeforeWordsReach101 = + "A very long long long long long long long long long long long long long long long" + + " loooooong sentence"; + } + + static class Test2LineReflowLimit { + String doesNotReflowEitherLinesAt100 = + "A very long long long long long long long long long long long long long looooong sentence." + + " And a second very long long long long long long long long long long loong sentence"; + String reflowsLastLineAt101 = + "A very long long long long long long long long long long long long long looooong sentence." + + " And a second very long long long long long long long long long long looong" + + " sentence"; + } + + static class TestWithTrailingCharacters { + String fitsLastLineUpTo100WithTrailingCharacters = + f( + f( + "A very long long long long long long long long long long long long loong sentence." + + " And a second very long long long long long long long long loong sentence")); + String reflowsLastLineToAccommodateTrailingCharacters = + f( + f( + "A very long long long long long long long long long long long long loong sentence." + + " And a second very long long long long long long long long looong" + + " sentence")); + // Tests an off-by-one issue, but see b/179561701 for a similar issue that is not yet fixed + String doesNotOverTriggerLastLineReflow = + f( + f( + "A very long long long long long long long long long long long long loong sentence." + + " And a second very loong sentence with trailing a a a a a a a a a a a a a a" + + " a")); + } +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.input index a1e07d1..15fc1b2 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.input @@ -7,7 +7,7 @@ package com.google.googlejavaformat.java.test; * SynchronizedStatements. */ class S { - // TODO(jdd): Add tests for higher language levels. + // TODO(user): Add tests for higher language levels. int x = 0; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.output index a1e07d1..15fc1b2 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/S.output @@ -7,7 +7,7 @@ package com.google.googlejavaformat.java.test; * SynchronizedStatements. */ class S { - // TODO(jdd): Add tests for higher language levels. + // TODO(user): Add tests for higher language levels. int x = 0; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.input index c8cd293..fc9bc09 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.input @@ -5,7 +5,7 @@ package com.google.googlejavaformat.java.test; * TypeDeclarations, TypeLiterals, TypeMethodReferences, TypeParameters, and Types. */ class T<T1, T2, T3> { - // TODO(jdd): Add tests for higher language levels. + // TODO(user): Add tests for higher language levels. T f(int x) throws Exception { class TT {} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.output index c8cd293..fc9bc09 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.output +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/T.output @@ -5,7 +5,7 @@ package com.google.googlejavaformat.java.test; * TypeDeclarations, TypeLiterals, TypeMethodReferences, TypeParameters, and Types. */ class T<T1, T2, T3> { - // TODO(jdd): Add tests for higher language levels. + // TODO(user): Add tests for higher language levels. T f(int x) throws Exception { class TT {} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input new file mode 100644 index 0000000..ddaa8f1 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.input @@ -0,0 +1,33 @@ +import org.checkerframework.checker.nullness.qual.Nullable; + +class TypeAnnotations { + + @Deprecated + public @Nullable Object foo() {} + + public @Deprecated Object foo() {} + + @Nullable Foo handle() { + @Nullable Bar bar = bar(); + try (@Nullable Baz baz = baz()) {} + } + + Foo( + @Nullable Bar // + param1, // + Baz // + param2) {} + + void g( + @Deprecated @Nullable ImmutableList<String> veryVeryLooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + @Deprecated @Nullable ImmutableList<String> veryVeryLoooooooooooooooooooooooooooooooooooooooooooooooooooooooong) {} + + @Deprecated @Nullable TypeAnnotations() {} + + enum Foo { + @Nullable + BAR; + } + + @Nullable @Nullable Object doubleTrouble() {} +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output new file mode 100644 index 0000000..8dd5d4e --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/TypeAnnotations.output @@ -0,0 +1,39 @@ +import org.checkerframework.checker.nullness.qual.Nullable; + +class TypeAnnotations { + + @Deprecated + public @Nullable Object foo() {} + + public @Deprecated Object foo() {} + + @Nullable Foo handle() { + @Nullable Bar bar = bar(); + try (@Nullable Baz baz = baz()) {} + } + + Foo( + @Nullable Bar // + param1, // + Baz // + param2) {} + + void g( + @Deprecated + @Nullable ImmutableList<String> + veryVeryLooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + @Deprecated + @Nullable ImmutableList<String> + veryVeryLoooooooooooooooooooooooooooooooooooooooooooooooooooooooong) {} + + @Deprecated + @Nullable + TypeAnnotations() {} + + enum Foo { + @Nullable + BAR; + } + + @Nullable @Nullable Object doubleTrouble() {} +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.input new file mode 100644 index 0000000..da6c01b --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.input @@ -0,0 +1,3 @@ +class B26306390 { + int resourceId = com.some.extremely.verbose.pkg.name.R.string.some_extremely_long_resource_identifier_that_exceeds_the_column_limit; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.output new file mode 100644 index 0000000..a21772f --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/b26306390.output @@ -0,0 +1,5 @@ +class B26306390 { + int resourceId = + com.some.extremely.verbose.pkg.name.R.string + .some_extremely_long_resource_identifier_that_exceeds_the_column_limit; +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-import-sorting b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-import-sorting index e6994f7..8d144c2 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-import-sorting +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-import-sorting @@ -6,7 +6,7 @@ import static org.junit.Assert.fail; import com.google.common.base.Preconditions; import java.util.List; import java.util.Set; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-unused-import-removal b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-unused-import-removal index 7d5df53..9b1d01f 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-unused-import-removal +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-and-unused-import-removal @@ -7,7 +7,7 @@ import org.junit.runners.JUnit4; import java.util.List; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import static org.junit.Assert.fail; import static com.google.truth.Truth.assertThat; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-only b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-only index 7d5df53..9b1d01f 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-only +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.formatting-only @@ -7,7 +7,7 @@ import org.junit.runners.JUnit4; import java.util.List; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import static org.junit.Assert.fail; import static com.google.truth.Truth.assertThat; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-and-formatting b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-and-formatting index 887d43c..d7bcd6d 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-and-formatting +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-and-formatting @@ -5,7 +5,7 @@ import static org.junit.Assert.fail; import com.google.common.base.Preconditions; import java.util.List; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-only b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-only index 88f83f1..a50a83e 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-only +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.imports-only @@ -5,7 +5,7 @@ import static org.junit.Assert.fail; import com.google.common.base.Preconditions; import java.util.List; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.input b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.input index be1eacd..dd992a3 100644 --- a/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.input +++ b/core/src/test/resources/com/google/googlejavaformat/java/testimports/A.input @@ -8,7 +8,7 @@ import org.junit.runners.JUnit4; import java.util.List; import java.util.Set; -import javax.annotations.Nullable; +import javax.annotation.Nullable; import static org.junit.Assert.fail; import static com.google.truth.Truth.assertThat; |