From 2edd7d9f18a4920110ada5d0b2a5ecb01b497c34 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 11:26:25 +0300 Subject: [PATCH 01/23] Remove every @SuppressWarnings but Immutable's Of the 110 annotations 105 hid nothing. 94 named their checks with palantir-baseline's for-rollout: prefix, which plain Error Prone does not take for the check's name: NullAway and the rest reported those lines all along. Others named checks this build does not run (TooManyArguments, ImmutablesStyle), IntelliJ inspections, or javac's unchecked lint, which is off. Without UnicodeEscape's, its one finding shows. The five on Doc's and Level's memoized suppliers stay: Immutable is an error, and the suppliers are mutable caches that the check rightly rejects. They need their own change. --- .../javaformat/gradle/FormatDiff.java | 1 - .../gradle/FormatterServiceLoader.java | 1 - .../PalantirJavaFormatProviderPlugin.java | 2 -- .../NativePalantirJavaFormatStep.java | 2 -- .../intellij/PalantirJavaFormatSettings.java | 7 +------ ...lantirJavaFormatFormattingServiceTest.java | 1 - .../BootstrappingFormatterService.java | 1 - .../com/palantir/javaformat/Newlines.java | 2 -- .../com/palantir/javaformat/OpsBuilder.java | 3 --- .../com/palantir/javaformat/doc/Comment.java | 1 - .../com/palantir/javaformat/doc/Level.java | 3 --- .../LevelDelimitedFlatValueDocVisitor.java | 1 - .../java/com/palantir/javaformat/doc/Obs.java | 1 - .../com/palantir/javaformat/doc/State.java | 2 -- .../java/CommandLineOptionsParser.java | 1 - .../palantir/javaformat/java/Formatter.java | 1 - .../javaformat/java/ImportOrderer.java | 1 - .../palantir/javaformat/java/JavaInput.java | 8 ------- .../javaformat/java/JavaInputAstVisitor.java | 21 ------------------- .../palantir/javaformat/java/JavaOutput.java | 3 --- .../palantir/javaformat/java/JavacTokens.java | 2 -- .../palantir/javaformat/java/JsonSink.java | 3 --- .../com/palantir/javaformat/java/Main.java | 1 - .../javaformat/java/ModifierOrderer.java | 3 --- .../javaformat/java/RemoveUnusedImports.java | 4 ---- .../javaformat/java/StringWrapper.java | 3 --- .../com/palantir/javaformat/java/Trees.java | 1 - .../java/java14/Java14InputAstVisitor.java | 4 ---- .../java/java21/Java21InputAstVisitor.java | 1 - .../javaformat/java/javadoc/CharStream.java | 1 - .../javaformat/java/javadoc/JavadocLexer.java | 7 ------- .../java/javadoc/JavadocWriter.java | 1 - .../javaformat/java/DiagnosticTest.java | 1 - .../javaformat/java/FormatterTest.java | 8 ------- .../java/JavadocFormattingTest.java | 1 - .../jupiter/ParameterizedClass.java | 3 --- 36 files changed, 1 insertion(+), 106 deletions(-) diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatDiff.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatDiff.java index 6ac871250..ec4fb6abd 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatDiff.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatDiff.java @@ -129,7 +129,6 @@ private static Path gitTopLevelDir(Path dir) throws IOException, InterruptedExce return Paths.get(gitCommand(dir, "git", "rev-parse", "--show-toplevel")); } - @SuppressWarnings("for-rollout:DefaultCharset") private static String gitCommand(Path dir, String... args) throws IOException, InterruptedException { Process process = new ProcessBuilder().command(args).directory(dir.toFile()).start(); diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceLoader.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceLoader.java index 52229d198..34d7d4c21 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceLoader.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/FormatterServiceLoader.java @@ -69,7 +69,6 @@ public static synchronized FormatterService load(Collection jars) { return LOADED.computeIfAbsent(key, _key -> loadUncached(jars)); } - @SuppressWarnings("for-rollout:NullAway") private static FormatterService loadUncached(Collection jars) { URL[] jarUris = jars.stream() .map(file -> { diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/PalantirJavaFormatProviderPlugin.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/PalantirJavaFormatProviderPlugin.java index ae5ebdb29..f699a91f0 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/PalantirJavaFormatProviderPlugin.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/PalantirJavaFormatProviderPlugin.java @@ -25,7 +25,6 @@ public final class PalantirJavaFormatProviderPlugin implements Plugin { static final String CONFIGURATION_NAME = "palantirJavaFormat"; - @SuppressWarnings({"for-rollout:GradleTypesAsFields", "for-rollout:NonAbstractGradleType"}) @Override public void apply(Project rootProject) { Preconditions.checkState( @@ -34,7 +33,6 @@ public void apply(Project rootProject) { rootProject.getPluginManager().apply(NativeImageFormatProviderPlugin.class); - @SuppressWarnings("for-rollout:ConfigurationAvoidanceRegistration") Configuration configuration = rootProject.getConfigurations().create(CONFIGURATION_NAME, conf -> { conf.setDescription("Internal configuration for resolving the open-java-format implementation"); conf.setCanBeConsumed(false); diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java index f4414386a..32c175f0d 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/NativePalantirJavaFormatStep.java @@ -47,12 +47,10 @@ static class State implements Serializable { // Kept for state serialization purposes. // For more details, see PalantirJavaFormatStep.State#jarSignature - @SuppressWarnings({"unused", "FieldCanBeLocal"}) private FileSignature execSignature; private final transient Supplier execSupplier; - @SuppressWarnings("for-rollout:NullAway") State(Supplier supplier) { this.execSupplier = supplier; } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 48420169d..96edb1ee4 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -37,20 +37,17 @@ import static java.util.Optional.ofNullable; -@SuppressWarnings("for-rollout:SameNameButDifferent") @State( name = "PalantirJavaFormatSettings", storages = {@Storage("open-java-format.xml")}) public class PalantirJavaFormatSettings implements PersistentStateComponent { - @SuppressWarnings("for-rollout:SameNameButDifferent") private State state = new State(); static PalantirJavaFormatSettings getInstance(Project project) { return project.getService(PalantirJavaFormatSettings.class); } - @SuppressWarnings("for-rollout:SameNameButDifferent") @Nullable @Override public State getState() { @@ -58,7 +55,7 @@ public State getState() { } @Override - public void loadState(@SuppressWarnings("for-rollout:SameNameButDifferent") State state) { + public void loadState(State state) { this.state = state; } @@ -167,7 +164,6 @@ public void setImplementationClassPath(@Nullable List value) { .map(strings -> strings.stream().map(URI::create).collect(Collectors.toList())); } - @SuppressWarnings("for-rollout:NullAway") public List getImplementationClassPath() { return implementationClassPath .map(paths -> paths.stream().map(URI::toString).collect(Collectors.toList())) @@ -185,7 +181,6 @@ public void setEnabled(@Nullable String enabledStr) { } } - @SuppressWarnings("for-rollout:NullAway") public String getEnabled() { return switch (enabled) { case ENABLED -> "true"; diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 64050f4d5..4a039bab0 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -127,7 +127,6 @@ private PsiFile createPsiFile(String path, String... contents) throws IOExceptio } private static final class DelegatingFormatter extends PalantirJavaFormatFormattingService { - @SuppressWarnings("DataFlowIssue") public DelegatingFormatter() { this.setPluginDescriptor(FormattingService.EP_NAME .findExtension(PalantirJavaFormatFormattingService.class) diff --git a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java index e46312c6b..f5b13f314 100644 --- a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java +++ b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java @@ -86,7 +86,6 @@ private ImmutableList getFormatReplacementsInternal(String input, C /* outputReplacements= */ true, ranges.stream().map(RangeUtils::toStringRange).collect(Collectors.toList())); - @SuppressWarnings("for-rollout:NullAway") Optional output = FormatterCommandRunner.runWithStdin(command.toArgs(), input, Optional.of(jdkPath.getParent())); if (output.isEmpty() || output.get().isEmpty()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java b/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java index 73b03ce73..05bb595d2 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java @@ -53,7 +53,6 @@ public static int hasNewlineAt(String input, int idx) { } /** Returns the terminating line break in the input, or {@code null} if the input does not end in a break. */ - @SuppressWarnings("for-rollout:NullAway") public static String getLineEnding(String input) { for (String b : BREAKS) { if (input.endsWith(b)) { @@ -160,7 +159,6 @@ private static final class LineIterator implements Iterator { private final String input; private final Iterator indices; - @SuppressWarnings("for-rollout:NullAway") private LineIterator(String input) { this.input = input; this.indices = lineOffsetIterator(input); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java b/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java index 7ff81101e..837c9fb87 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java @@ -50,7 +50,6 @@ public final class OpsBuilder { /** Return the actual size of the AST node at position, including comments. */ public int actualSize(int position, int length) { Input.Token startToken = input.getPositionTokenMap().get(position); - @SuppressWarnings("for-rollout:NullAway") int start = startToken.getTok().getPosition(); for (Input.Tok tok : startToken.getToksBefore()) { if (tok.isComment()) { @@ -58,7 +57,6 @@ public int actualSize(int position, int length) { } } Input.Token endToken = input.getPositionTokenMap().get(position + length - 1); - @SuppressWarnings("for-rollout:NullAway") int end = endToken.getTok().getPosition() + endToken.getTok().length(); for (Input.Tok tok : endToken.getToksAfter()) { if (tok.isComment()) { @@ -71,7 +69,6 @@ public int actualSize(int position, int length) { /** Return the start column of the token at {@code position}, including leading comments. */ public Integer actualStartColumn(int position) { Input.Token startToken = input.getPositionTokenMap().get(position); - @SuppressWarnings("for-rollout:NullAway") int start = startToken.getTok().getPosition(); int line0 = input.getLineNumber(start); for (Input.Tok tok : startToken.getToksBefore()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Comment.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Comment.java index 37742b128..8297503a4 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Comment.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Comment.java @@ -157,7 +157,6 @@ protected Range computeRange() { public State computeBreaks( CommentsHelper commentsHelper, int maxWidth, State state, Obs.ExplorationNode observationNode) { String text = commentsHelper.rewrite(tok, maxWidth, state.column()); - @SuppressWarnings("for-rollout:NullAway") int lastLineStart = Iterators.getLast(Newlines.lineOffsetIterator(text)); int lastLineLength = text.length() - lastLineStart; // rewrite() indents every line after the first to state.column(), so after a comment that spans lines the diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java index 2824bdb9d..ce78867d6 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java @@ -402,7 +402,6 @@ private Optional tryInlinePrefixOntoCurrentLine( commentsHelper, maxWidth, newState, memoizedSplitsBreaks.get(), explorationNode)); } - @SuppressWarnings("for-rollout:NullAway") private Optional tryBreakLastLevel( CommentsHelper commentsHelper, int maxWidth, @@ -429,7 +428,6 @@ private Optional tryBreakLastLevel( /** A call of {@link #tryBreakLastLevel}, by everything besides this level that decides its outcome. */ private record LastLevelAttempt(int maxWidth, boolean isSimpleInliningSoFar, State.LayoutInputs inputs) {} - @SuppressWarnings("for-rollout:NullAway") private Optional tryInlineSuffix( CommentsHelper commentsHelper, int maxWidth, @@ -549,7 +547,6 @@ private static Optional tryBreakInnerLevel_acceptInlineChain( .markAccepted()); } - @SuppressWarnings("for-rollout:NullAway") private static Optional tryBreakInnerLevel_checkInner( CommentsHelper commentsHelper, int maxWidth, diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/LevelDelimitedFlatValueDocVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/LevelDelimitedFlatValueDocVisitor.java index 5ef16ab9b..8de02dac4 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/LevelDelimitedFlatValueDocVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/LevelDelimitedFlatValueDocVisitor.java @@ -53,7 +53,6 @@ public String visitBreak(Break doc) { return sb.toString(); } - @SuppressWarnings("for-rollout:NullAway") @Override public String visitLevel(Level level) { if (level.getFlat().isEmpty()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java index 457351e08..0fcbf7ede 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java @@ -64,7 +64,6 @@ FinishExplorationNode startExplorationNode( String getOutput(); } - @SuppressWarnings("for-rollout:NullAway") public static ExplorationNode createRoot(Sink sink) { return new ExplorationNodeImpl(null, "(initial node)", sink, 0, Optional.empty()); } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/State.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/State.java index d5b53f13b..02b523d78 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/State.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/State.java @@ -26,8 +26,6 @@ import org.immutables.value.Value; /** State for writing. */ -// Automatically suppressed to unblock enforcement in new code -@SuppressWarnings("ImmutablesStyle") @Value.Immutable @Value.Style(overshadowImplementation = true) @JsonSerialize(as = ImmutableState.class) diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java index 7a1dd0a36..55330717e 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java @@ -55,7 +55,6 @@ final class CommandLineOptionsParser { ); /** Parses {@link CommandLineOptions}. */ - @SuppressWarnings("for-rollout:NullAway") static CommandLineOptions parse(Iterable options) { CommandLineOptions.Builder optionsBuilder = CommandLineOptions.builder(); List expandedOptions = new ArrayList<>(); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java index ebc10209d..ed2549241 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java @@ -243,7 +243,6 @@ static int getRuntimeVersion() { return Runtime.version().feature(); } - @SuppressWarnings("for-rollout:ThrowError") private static JavaInputAstVisitor createVisitor( final String className, final OpsBuilder builder, final JavaFormatterOptions options) { try { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/ImportOrderer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/ImportOrderer.java index 3478db685..7d58138b3 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/ImportOrderer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/ImportOrderer.java @@ -93,7 +93,6 @@ private String reorderImports() throws FormatterException { tail.add(CharMatcher.whitespace().trimLeadingFrom(tokString(afterLastImport, toks.size()))); if (!toks.isEmpty()) { Tok lastTok = getLast(toks); - @SuppressWarnings("for-rollout:NullAway") int tailStart = lastTok.getPosition() + lastTok.length(); tail.add(text.substring(tailStart)); } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java index de951b0ca..950ae3ac7 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java @@ -59,7 +59,6 @@ import javax.tools.SimpleJavaFileObject; /** {@code JavaInput} extends {@link Input} to represent a Java input document. */ -@SuppressWarnings("for-rollout:ThrowError") public final class JavaInput extends Input { /** * A {@code JavaInput} is a sequence of {@link Tok}s that cover the Java input. A {@link Tok} is either a token (if @@ -258,7 +257,6 @@ public String toString() { * output. */ - @SuppressWarnings("for-rollout:AlmostJavadoc") private final ImmutableMap positionToColumnMap; // Map Tok position to column. private final ImmutableList tokens; // The Tokens for this input. @@ -273,7 +271,6 @@ public String toString() { * @param text the input text * @throws FormatterException if the input cannot be parsed */ - @SuppressWarnings("for-rollout:NullAway") public JavaInput(String text) throws FormatterException { this.text = checkNotNull(text); setLines(ImmutableList.copyOf(Newlines.lineIterator(text))); @@ -338,7 +335,6 @@ public String getLineSeparator() { } /** Lex the input and build the list of toks. */ - @SuppressWarnings("for-rollout:NullAway") private ImmutableList buildToks(String text) throws FormatterException { ImmutableList toks = buildToks(text, ImmutableSet.of()); kN = getLast(toks).getIndex(); @@ -353,7 +349,6 @@ private ImmutableList buildToks(String text) throws FormatterException { * @param stopTokens a set of tokens which should cause lexing to stop. If one of these is found, the returned list * will include tokens up to but not including that token. */ - @SuppressWarnings("for-rollout:NullAway") static ImmutableList buildToks(String text, ImmutableSet stopTokens) throws FormatterException { stopTokens = ImmutableSet.builder() .addAll(stopTokens) @@ -481,7 +476,6 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept * @param handler the diagnostic handler * @return a collection of diagnostics */ - @SuppressWarnings({"unchecked", "for-rollout:ThrowError"}) private static Collection getDiagnostics(DeferredDiagnosticHandler handler) { try { return (Collection) GET_DIAGNOSTICS.invoke(handler); @@ -549,7 +543,6 @@ private static boolean isSpaces(Tok tok) { return !tok.isToken() && !tok.isNewline() && !tok.isComment(); } - @SuppressWarnings("for-rollout:NullAway") private static int updateColumn(int columnI, String originalTokText) { Integer last = Iterators.getLast(Newlines.lineOffsetIterator(originalTokText)); if (last > 0) { @@ -642,7 +635,6 @@ private static boolean isParamComment(Tok tok) { * @return the {@code 0}-based {@link Range} of tokens * @throws FormatterException on formatting errors */ - @SuppressWarnings("for-rollout:NullAway") Range characterRangeToTokenRange(int offset, int length) throws FormatterException { int requiredLength = offset + length; if (requiredLength > text.length()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index ad0f8df96..2c374113b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -991,7 +991,6 @@ void visitVariables(List fragments, DeclarationKind declarationKin } } - @SuppressWarnings("for-rollout:NullAway") private TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) { if (type == null) { return null; @@ -1004,7 +1003,6 @@ private TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree t null, leadingDims > 0 ? dims.dims.subList(0, dims.dims.size() - leadingDims) : dims.dims); } - @SuppressWarnings("for-rollout:NullAway") @Override public Void visitForLoop(ForLoopTree node, Void unused) { sync(node); @@ -1406,7 +1404,6 @@ private static boolean isCompactSourceFile(Tree type) { && (classDecl.mods.flags & IMPLICIT_CLASS) == IMPLICIT_CLASS; } - @SuppressWarnings("for-rollout:NullAway") @Override public Void visitMethod(MethodTree node, Void unused) { sync(node); @@ -1649,7 +1646,6 @@ private static List handleStream(List parts) { .collect(toList()); } - @SuppressWarnings("for-rollout:NullAway") private static Stream indexes(Stream stream, Predicate predicate) { return Streams.mapWithIndex(stream, (x, i) -> predicate.apply(x) ? i : -1) .filter(x -> x != -1); @@ -1825,7 +1821,6 @@ public Void visitReturn(ReturnTree node, Void unused) { } // TODO(cushon): is this worth special-casing? - @SuppressWarnings("for-rollout:NullAway") boolean visitSingleMemberAnnotation(AnnotationTree node) { if (node.getArguments().size() != 1) { return false; @@ -1851,7 +1846,6 @@ boolean visitSingleMemberAnnotation(AnnotationTree node) { return true; } - @SuppressWarnings("for-rollout:deprecation") @Override public Void visitCase(CaseTree node, Void unused) { sync(node); @@ -2204,7 +2198,6 @@ private void visitStatement( } } - @SuppressWarnings("for-rollout:NullAway") protected void visitStatements(List statements, boolean inlineFirst) { boolean first = true; PeekingIterator it = Iterators.peekingIterator(statements.iterator()); @@ -2219,7 +2212,6 @@ protected void visitStatements(List statements, boolean } markForPartialFormat(); first = false; - @SuppressWarnings("for-rollout:NullAway") List fragments = variableFragments(it, tree); if (!fragments.isEmpty()) { visitVariables( @@ -2944,7 +2936,6 @@ private void visitRegularDot(List items, boolean needDot) { // .happens()) // .thenReturn(result); // - @SuppressWarnings("for-rollout:NullAway") private boolean fillFirstArgument(ExpressionTree e, List items, Indent indent) { // is there a trailing dereference? if (items.size() < 2) { @@ -2984,7 +2975,6 @@ private boolean fillFirstArgument(ExpressionTree e, List items, private void visitDotWithPrefix( List items, boolean needDot, Collection prefixes, FillMode prefixFillMode) { // Are there method invocations or field accesses after the prefix? - @SuppressWarnings("for-rollout:NullAway") boolean trailingDereferences = !prefixes.isEmpty() && getLast(prefixes) < items.size() - 1; boolean hasMethodInvocations = items.stream().anyMatch(expr -> expr instanceof MethodInvocationTree); @@ -3120,7 +3110,6 @@ private ExpressionTree getArrayBase(ExpressionTree node) { return node; } - @SuppressWarnings("for-rollout:NullAway") private ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { ExpressionTree select = methodInvocation.getMethodSelect(); return select instanceof MemberSelectTree memberSelectTree ? memberSelectTree.getExpression() : null; @@ -3364,14 +3353,12 @@ public void scan(JCTree tree) { } /** Returns the number of columns if the arguments arg laid out in a grid, or else {@code -1}. */ - @SuppressWarnings("for-rollout:NullAway") private int argumentsAreTabular(List arguments) { if (arguments.isEmpty()) { return -1; } List> rows = new ArrayList<>(); PeekingIterator it = Iterators.peekingIterator(arguments.iterator()); - @SuppressWarnings("for-rollout:NullAway") int start0 = actualColumn(it.peek()); { List row = new ArrayList<>(); @@ -3389,7 +3376,6 @@ private int argumentsAreTabular(List arguments) { } while (it.hasNext()) { List row = new ArrayList<>(); - @SuppressWarnings("for-rollout:NullAway") int start = actualColumn(it.peek()); if (start != start0) { return -1; @@ -3444,14 +3430,12 @@ static int rowLength(List row) { return size; } - @SuppressWarnings("for-rollout:NullAway") private Integer actualColumn(ExpressionTree expression) { Map positionToColumnMap = builder.getInput().getPositionToColumnMap(); return positionToColumnMap.get(builder.actualStartColumn(getStartPosition(expression))); } /** How many lines does this node take up in the input. Returns at least 1. */ - @SuppressWarnings("for-rollout:NullAway") int lineSpan(Tree node) { ImmutableRangeMap positionTokenMap = builder.getInput().getPositionTokenMap(); @@ -3505,7 +3489,6 @@ enum DeclarationKind { } /** Declare one variable or variable-like thing. */ - @SuppressWarnings("TooManyArguments") int declareOne( DeclarationKind kind, Direction annotationsDirection, @@ -3534,7 +3517,6 @@ int declareOne( } /** Declare one variable or variable-like thing. */ - @SuppressWarnings("TooManyArguments") int declareOne( DeclarationKind kind, Direction annotationsDirection, @@ -3792,7 +3774,6 @@ protected void addBodyDeclarations( * class/interface/enum bodies (indented by {@link #plusTwo}) and for the bare top-level member list of a * compact source file (JEP 512), which is indented by {@link Indent.Const#ZERO}. */ - @SuppressWarnings("for-rollout:NullAway") private void addBodyDeclarations( List bodyDeclarations, BracesOrNot braces, @@ -3931,7 +3912,6 @@ private boolean hasTrailingToken(Input input, List nodes, String return false; } Tree lastNode = getLast(nodes); - @SuppressWarnings("for-rollout:NullAway") Optional nextToken = getNextToken(input, getEndPosition(lastNode, getCurrentPath())); return nextToken.isPresent() && nextToken.get().getTok().getText().equals(token); } @@ -4025,7 +4005,6 @@ protected void sync(Tree node) { * have it. Used with {@link #invoke} to reach AST accessors that are newer than the compiler this module builds * with, such as {@code ImportTree#isModule()} (JDK 23). */ - @SuppressWarnings("for-rollout:NullAway") protected static Method maybeGetMethod(Class c, String name) { try { return c.getMethod(name); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java index 1b254f680..9466bd12b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java @@ -242,7 +242,6 @@ void flush() { * * @return a list of {@link Replacement}s, sorted by start index, without overlaps */ - @SuppressWarnings("for-rollout:NullAway") public ImmutableList getFormatReplacements(RangeSet iRangeSet0) { ImmutableList.Builder result = ImmutableList.builder(); Map> kToJ = JavaOutput.makeKToIJ(this); @@ -278,7 +277,6 @@ public ImmutableList getFormatReplacements(RangeSet iRange replaceFrom--; } - @SuppressWarnings("for-rollout:NullAway") int i = kToJ.get(startTok.getIndex()).lowerEndpoint(); // Include leading blank lines from the formatted output, unless the formatted range // starts at the beginning of the file. @@ -357,7 +355,6 @@ public ImmutableList getFormatReplacements(RangeSet iRange * @param iRange the {@link Range} of tokens * @return the expanded token range */ - @SuppressWarnings("for-rollout:NullAway") private Range expandToBreakableRegions(Range iRange) { // The original line range. int loTok = iRange.lowerEndpoint(); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java index 705837412..9cc61d20a 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java @@ -72,7 +72,6 @@ public String stringVal() { } /** Lex the input and return a list of {@link RawTok}s. */ - @SuppressWarnings("for-rollout:NullAway") public static ImmutableList getTokens(String source, Context context, Set stopTokens) { if (source == null) { return ImmutableList.of(); @@ -155,7 +154,6 @@ static class CommentWithTextAndPosition implements Comment { private final AccessibleReader reader; private final CommentStyle style; - @SuppressWarnings("for-rollout:NullAway") private String text = null; public CommentWithTextAndPosition(int pos, int endPos, AccessibleReader reader, CommentStyle style) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java index 6aa69eac2..edb40b3a0 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java @@ -37,10 +37,8 @@ public final class JsonSink implements Sink { private final Map childrenMap = new HashMap<>(); - @SuppressWarnings("for-rollout:NullAway") private ObjectNode rootNode; - @SuppressWarnings("for-rollout:NullAway") @Override public FinishExplorationNode startExplorationNode( int exporationId, @@ -72,7 +70,6 @@ public FinishExplorationNode startExplorationNode( @Override public FinishLevelNode writeLevelNode(int levelNodeId, int parentExplorationId, State incomingState, Level level) { - @SuppressWarnings("for-rollout:NullAway") ObjectNode json = childrenMap.get(parentExplorationId).addObject(); json.put("type", "level"); json.put("id", levelNodeId); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java index 5551b325d..903dfc6e3 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java @@ -129,7 +129,6 @@ private int formatFiles(CommandLineOptions parameters, JavaFormatterOptions opti } } - @SuppressWarnings("for-rollout:RedundantControlFlow") private int formatFiles( CommandLineOptions parameters, JavaFormatterOptions options, ExecutorService executorService) { Map inputs = new LinkedHashMap<>(); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java index fe97403ac..3fa532e6e 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java @@ -71,7 +71,6 @@ boolean isEmpty() { return tokens.isEmpty() || modifier == null; } - @SuppressWarnings("for-rollout:NullAway") Modifier modifier() { return modifier; } @@ -105,7 +104,6 @@ ImmutableList getToksAfter() { } @Override - @SuppressWarnings("for-rollout:NullAway") public int compareTo(ModifierTokens o) { checkState(!isEmpty()); // empty ModifierTokens are filtered out prior to sorting return modifier.compareTo(o.modifier); @@ -209,7 +207,6 @@ private static Modifier asModifier(ImmutableList tokens) { * Returns the given token as a {@link javax.lang.model.element.Modifier}, or {@code null} if it is not a modifier. */ @Nullable - @SuppressWarnings("for-rollout:NullAway") private static Modifier asModifier(Token token) { TokenKind kind = ((JavaInput.Tok) token.getTok()).kind(); if (kind != null) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java index a42aee5ec..c27745e4f 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java @@ -103,7 +103,6 @@ public Void visitIdentifier(IdentifierTree tree, Void unused) { // TODO(fwindheuser): 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) { @@ -118,7 +117,6 @@ public Void visitCase(CaseTree tree, Void unused) { private static final Method CASE_TREE_GET_LABELS = caseTreeGetLabels(); - @SuppressWarnings("for-rollout:NullAway") private static Method caseTreeGetLabels() { try { return CaseTree.class.getMethod("getLabels"); @@ -127,7 +125,6 @@ private static Method caseTreeGetLabels() { } } - @SuppressWarnings("for-rollout:VoidUsed") @Override public Void scan(Tree tree, Void unused) { if (tree == null) { @@ -185,7 +182,6 @@ public ReferenceScanner(long basePos) { this.basePos = basePos; } - @SuppressWarnings("for-rollout:VoidUsed") @Override public Void visitIdentifier(IdentifierTree node, Void aVoid) { usedInJavadoc.put( diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index d5472baa8..26af58a52 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -130,7 +130,6 @@ private static String wrapOnce(final int columnLimit, String input, Formatter fo return result; } - @SuppressWarnings("ResultOfMethodCallIgnored") private static ImmutableSet> rangesAfterAppliedReplacements( TreeRangeMap replacements) { ImmutableSet.Builder> outputRanges = ImmutableSet.builder(); @@ -482,7 +481,6 @@ private record Chain( int firstLineColumn, List plain) {} - @SuppressWarnings("for-rollout:NullAway") private void wrapLongStrings( TreeRangeMap replacements, List longStringLiterals, @@ -516,7 +514,6 @@ private void wrapLongStrings( // Handling leaving trailing non-string tokens at the end of the literal, // e.g. the trailing `);` in `foo("...");`. - @SuppressWarnings("for-rollout:NullAway") int end = getEndPosition(unit, getLast(flat)); int lineEnd = end; while (Newlines.hasNewlineAt(input, lineEnd) == -1) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java index d7fd65e2b..9d573102a 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java @@ -142,7 +142,6 @@ static Name getMethodName(MethodInvocationTree methodInvocation) { } /** Returns the receiver of a qualified method invocation expression, or {@code null}. */ - @SuppressWarnings("for-rollout:NullAway") static ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { ExpressionTree select = methodInvocation.getMethodSelect(); return select instanceof MemberSelectTree memberSelectTree ? memberSelectTree.getExpression() : null; diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java index 746a8e2be..178fd0987 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java @@ -51,7 +51,6 @@ /** * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for Java 14. */ -@SuppressWarnings("Since15") public class Java14InputAstVisitor extends JavaInputAstVisitor { private static final Method COMPILATION_UNIT_TREE_GET_MODULE = maybeGetMethod(CompilationUnitTree.class, "getModule"); @@ -94,7 +93,6 @@ protected List getPermitsClause(ClassTree node) { } } - @SuppressWarnings("for-rollout:NullAway") @Override public Void visitBindingPattern(BindingPatternTree node, Void unused) { sync(node); @@ -252,7 +250,6 @@ public Void visitInstanceOf(InstanceOfTree node, Void unused) { return null; } - @SuppressWarnings("for-rollout:NullAway") @Override public Void visitCase(CaseTree node, Void unused) { sync(node); @@ -347,7 +344,6 @@ public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) { return null; } - @SuppressWarnings({"NullableProblems", "for-rollout:NullAway"}) protected ExpressionTree getGuard(final CaseTree node) { return null; } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java index f6f586ee7..703b3ac10 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java @@ -32,7 +32,6 @@ * Extends {@link Java14InputAstVisitor} with support for AST nodes that were added or modified in * Java 21. */ -@SuppressWarnings("Since21") public class Java21InputAstVisitor extends Java14InputAstVisitor { // AnyPatternTree (the unnamed pattern `_`, JEP 456) is still a preview API on JDK 21, which this // module compiles with, so match it by Tree.Kind name instead of by type. diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/CharStream.java b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/CharStream.java index 2664aa3aa..f74d8eccf 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/CharStream.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/CharStream.java @@ -45,7 +45,6 @@ boolean tryConsume(String expected) { /* * @param pattern the pattern to search for, which must be anchored to match only at position 0 */ - @SuppressWarnings("for-rollout:AlmostJavadoc") boolean tryConsumeRegex(Pattern pattern) { Matcher matcher = pattern.matcher(remaining); if (!matcher.find()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java index c67b0f1ae..f0fa8c663 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java @@ -320,7 +320,6 @@ private void checkMatchingTags() throws LexException { * *

Note that we do not merge a literal token and a whitespace token together. */ - @SuppressWarnings("for-rollout:NullAway") private static ImmutableList joinAdjacentLiteralsAndAdjacentWhitespace(List input) { /* * Note: Our final token is always END_JAVADOC. This saves us some trouble: @@ -335,7 +334,6 @@ private static ImmutableList joinAdjacentLiteralsAndAdjacentWhitespace(Li boolean lastTagWasInlineTagStart = false; for (PeekingIterator tokens = peekingIterator(input.iterator()); tokens.hasNext(); ) { - @SuppressWarnings("for-rollout:NullAway") Token.Type nextType = tokens.peek().getType(); if (nextType == INLINE_TAG_OPEN) { inlineTagDepth.increment(); @@ -429,7 +427,6 @@ private static boolean isSuspendedHyphenConjunction(String s) { *

This method must be called after {@link #joinAdjacentLiteralsAndAdjacentWhitespace}, as it assumes that * adjacent whitespace tokens have already been joined. */ - @SuppressWarnings("for-rollout:NullAway") private static ImmutableList inferParagraphTags(List input) { ImmutableList.Builder output = ImmutableList.builder(); @@ -468,7 +465,6 @@ && hasMultipleNewlines(tokens.peek().getValue())) { *

This method must be called after {@link #joinAdjacentLiteralsAndAdjacentWhitespace}, as it assumes that * adjacent whitespace tokens have already been joined. */ - @SuppressWarnings("for-rollout:NullAway") private static ImmutableList optionalizeSpacesAfterLinks(List input) { ImmutableList.Builder output = ImmutableList.builder(); @@ -498,7 +494,6 @@ private static ImmutableList optionalizeSpacesAfterLinks(List inpu * *

Also trim leading and trailing blank lines, and move the trailing `}` to its own line. */ - @SuppressWarnings("for-rollout:NullAway") private static ImmutableList deindentPreCodeBlocks(List input) { ImmutableList.Builder output = ImmutableList.builder(); for (PeekingIterator tokens = peekingIterator(input.iterator()); tokens.hasNext(); ) { @@ -523,7 +518,6 @@ private static ImmutableList deindentPreCodeBlocks(List input) { return output.build(); } - @SuppressWarnings("for-rollout:NullAway") private static void deindentPreCodeBlock(ImmutableList.Builder output, PeekingIterator tokens) { Deque saved = new ArrayDeque<>(); output.add(new Token(LITERAL, tokens.next().getValue().trim())); @@ -635,7 +629,6 @@ private static boolean hasMultipleNewlines(String s) { * with matching only one character here. That would eliminate the need for the regex entirely. * That might be faster or slower than what we do now. */ - @SuppressWarnings("for-rollout:AlmostJavadoc") private static final Pattern LITERAL_PATTERN = compile("^.[^ \t\n@<{}*]*", DOTALL); private static Pattern fullCommentPattern() { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java index 47be68fdb..b9071173a 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocWriter.java @@ -280,7 +280,6 @@ private void requestNewline() { requestWhitespace(NEWLINE); } - @SuppressWarnings("for-rollout:NullAway") private void requestWhitespace(RequestedWhitespace requestedWhitespace) { this.requestedWhitespace = Ordering.natural().max(requestedWhitespace, this.requestedWhitespace); } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java index 5961a4335..6d83af8aa 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java @@ -54,7 +54,6 @@ void restoreLocale() throws Exception { @Test void parseError() throws Exception { - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String input = Joiner.on('\n') .join( "public class InvalidSyntax {", diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java index 68f462e27..aec3257ab 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java @@ -69,9 +69,7 @@ void formatNonJavaFiles() throws Exception { @Test void formatStdinStdoutWithDashFlag() throws Exception { - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n"; - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; InputStream in = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); @@ -90,9 +88,7 @@ void formatStdinStdoutWithDashFlag() throws Exception { @Test void formatLengthUpToEOF() throws Exception { - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n\n\n\n\n\n"; - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; Path path = testFolder.resolve("Foo.java"); @@ -131,7 +127,6 @@ void blankInClassBodyNoTrailing() throws FormatterException { assertThat(output).isEqualTo(expect); } - @SuppressWarnings("for-rollout:StringConcatToTextBlock") private static final String UNORDERED_IMPORTS = Joiner.on('\n') .join( "import com.google.common.base.Preconditions;", @@ -220,7 +215,6 @@ void throwsFormatterException() throws Exception { @Test void blankLinesImportComment() throws FormatterException { - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String withBlank = "package p;\n" + "\n" + "/** test */\n" @@ -230,7 +224,6 @@ void blankLinesImportComment() throws FormatterException { + "class T {\n" + " A a;\n" + "}\n"; - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String withoutBlank = "package p;\n" + "\n" + "/** test */\n" + "import a.A;\n" + "\n" + "class T {\n" + " A a;\n" + "}\n"; @@ -259,7 +252,6 @@ void canParse_java9_private_interface_methods() { @Test void producesFormattingChangesOnAlreadyFormattedFiles() throws FormatterException { Formatter formatter = FORMATTER; - @SuppressWarnings("for-rollout:StringConcatToTextBlock") String simpleClass = "package com.palantir;\n" + "\n" + "public final class Formatted {\n" diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java index 80394ce20..0643bfce0 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/JavadocFormattingTest.java @@ -1147,7 +1147,6 @@ void unicodeEscapesNotReplaced() { } @Test - @SuppressWarnings("UnicodeEscape") void unicodeEscapesNotInterpretedBug() { /* * In theory, "\u003C" should be treated exactly like <, and so too should the escaped versions of diff --git a/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java b/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java index 6bd5cf22f..fc3540ac7 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java @@ -167,7 +167,6 @@ public boolean supportsTestTemplate(ExtensionContext _value) { } /** Users must provide a public static Object[][] method, and this invokes it *once*, caching the result. */ - @SuppressWarnings("for-rollout:deprecation") private static List invokeUserParametersMethod(ExtensionContext extensionContext, Class testClass) { List methods = AnnotationUtils.findAnnotatedMethods( testClass, Parameters.class, ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP); @@ -180,7 +179,6 @@ private static List invokeUserParametersMethod(ExtensionContext extens "invokeUserParametersMethod", unused -> allParameters(userParametersMethod), List.class); } - @SuppressWarnings("unchecked") private static List allParameters(Method method) { try { Object parameters = method.invoke(null); @@ -204,7 +202,6 @@ private static List allParameters(Method method) { } } - @SuppressWarnings("for-rollout:deprecation") private static String findStringFormatTemplate(ExtensionContext extensionContext, Class testClass) { return extensionContext .getStore(namespace) From ffa9b7f3eb969e4d2bbb913b43dfd7c07bc3047a Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:13:09 +0300 Subject: [PATCH 02/23] Make UnusedVariable an error ExecutableTransform's logger, StringWrapper's endLine and the stderr two MainTest tests read after the process had exited were never read. They are gone, and Error Prone now fails the build on the next one. --- .../groovy/open-java-format.error-prone-conventions.gradle | 7 +++++++ .../palantir/javaformat/gradle/ExecutableTransform.java | 4 ---- .../java/com/palantir/javaformat/java/StringWrapper.java | 1 - .../test/java/com/palantir/javaformat/java/MainTest.java | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 9bb1278d3..a1814b01f 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -51,3 +51,10 @@ tasks.withType(JavaCompile).configureEach { //disable('NonStaticImport') } } + +// Checks whose findings have been fixed, build-breaking so that new ones are fixed too rather than read past. +tasks.withType(JavaCompile).configureEach { + options.errorprone { + error('UnusedVariable') + } +} diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/ExecutableTransform.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/ExecutableTransform.java index 947685bf8..6c5db8178 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/ExecutableTransform.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/ExecutableTransform.java @@ -31,8 +31,6 @@ import org.gradle.api.artifacts.transform.TransformOutputs; import org.gradle.api.artifacts.transform.TransformParameters; import org.gradle.api.file.FileSystemLocation; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; @@ -47,8 +45,6 @@ because = "Only copies one file and sets its executable bit; caching would cost more than it saves") public abstract class ExecutableTransform implements TransformAction { - private static Logger logger = Logging.getLogger(ExecutableTransform.class); - @InputArtifact @PathSensitive(PathSensitivity.NAME_ONLY) public abstract Provider getInputArtifact(); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index 26af58a52..6034b35a1 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -448,7 +448,6 @@ private String computePrefixIndentation( int startLine = lineMap.getLineNumber(startingPos); int lineStartPosition = lineMap.getStartPosition(startLine); int endPos = getEndPosition(unit, expression); - int endLine = lineMap.getLineNumber(endPos); int parentLine = shouldUseStartLineParent ? startParentLine : endParentLine; // ignore indentation if the current argument is on the same line as the parent if (startLine == parentLine) { diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java index 3f675eac5..800f25998 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java @@ -483,7 +483,6 @@ void exitIfChangedStdin() throws Exception { Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); Process process = formatterMain("-").redirectInput(path.toFile()).start(); process.waitFor(); - String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); assertThat(out).isEqualTo("" + System.lineSeparator()); assertThat(process.exitValue()).isEqualTo(1); @@ -495,7 +494,6 @@ void exitIfChangedFiles() throws Exception { Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); Process process = formatterMain(path.toAbsolutePath().toString()).start(); process.waitFor(); - String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); assertThat(out).isEqualTo(path.toAbsolutePath().toString() + System.lineSeparator()); assertThat(process.exitValue()).isEqualTo(1); From a16cf321a269bdb2fe77c6364301bbd7c07a5413 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:13:20 +0300 Subject: [PATCH 03/23] Make SameNameButDifferent an error In PalantirJavaFormatSettings `State` named both IntelliJ's @State annotation and the settings' own State class. The annotation is now written out in full, which leaves the name to the class. --- .../groovy/open-java-format.error-prone-conventions.gradle | 1 + .../javaformat/intellij/PalantirJavaFormatSettings.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index a1814b01f..83143fb71 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -55,6 +55,7 @@ tasks.withType(JavaCompile).configureEach { // Checks whose findings have been fixed, build-breaking so that new ones are fixed too rather than read past. tasks.withType(JavaCompile).configureEach { options.errorprone { + error('SameNameButDifferent') error('UnusedVariable') } } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 96edb1ee4..c5cadcfdc 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -18,7 +18,6 @@ import com.intellij.formatting.service.FormattingService; import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; @@ -37,7 +36,7 @@ import static java.util.Optional.ofNullable; -@State( +@com.intellij.openapi.components.State( name = "PalantirJavaFormatSettings", storages = {@Storage("open-java-format.xml")}) public class PalantirJavaFormatSettings implements PersistentStateComponent { From 821d50cafec171e4896ebeef4ebd0f3459ec8ef5 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:13:20 +0300 Subject: [PATCH 04/23] Make RefactorSwitch an error The switch statements in DimensionHelpers.extractDims, CommandLineOptionsParser's two range parsers and SnippetFormatter's snippetWrapper returned from every case. Each is now the switch expression it returns, with yield where a case has statements before its value. --- ...en-java-format.error-prone-conventions.gradle | 1 + .../javaformat/java/SnippetFormatter.java | 12 ++++++------ .../java/CommandLineOptionsParser.java | 16 ++++++++-------- .../javaformat/java/DimensionHelpers.java | 16 ++++++---------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 83143fb71..cb7504a5c 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -55,6 +55,7 @@ tasks.withType(JavaCompile).configureEach { // Checks whose findings have been fixed, build-breaking so that new ones are fixed too rather than read past. tasks.withType(JavaCompile).configureEach { options.errorprone { + error('RefactorSwitch') error('SameNameButDifferent') error('UnusedVariable') } diff --git a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java index b1457c261..51d1e3410 100644 --- a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java +++ b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java @@ -159,7 +159,7 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi * dummy class is correctly formatted -- the blocks use correct indentation, * etc. */ - switch (kind) { + return switch (kind) { case COMPILATION_UNIT -> { SnippetWrapper wrapper = new SnippetWrapper(); for (int i = 1; i <= initialIndent; i++) { @@ -167,7 +167,7 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi } wrapper.appendSource(source); wrapper.closeBraces(initialIndent); - return wrapper; + yield wrapper; } case CLASS_BODY_DECLARATIONS -> { SnippetWrapper wrapper = new SnippetWrapper(); @@ -176,7 +176,7 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi } wrapper.appendSource(source); wrapper.closeBraces(initialIndent); - return wrapper; + yield wrapper; } case STATEMENTS -> { SnippetWrapper wrapper = new SnippetWrapper(); @@ -186,7 +186,7 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi } wrapper.appendSource(source); wrapper.closeBraces(initialIndent); - return wrapper; + yield wrapper; } case EXPRESSION -> { SnippetWrapper wrapper = new SnippetWrapper(); @@ -198,9 +198,9 @@ private SnippetWrapper snippetWrapper(SnippetKind kind, String source, int initi wrapper.appendSource(source); wrapper.append(";"); wrapper.closeBraces(initialIndent); - return wrapper; + yield wrapper; } default -> throw new IllegalArgumentException("Unknown snippet kind: " + kind); - } + }; } } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java index 55330717e..06088b223 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java @@ -141,18 +141,18 @@ private static void parseCharacterRanges(ImmutableRangeSet.Builder resu */ private static Range parseCharacterRange(String range) { List args = COLON_SPLITTER.splitToList(range); - switch (args.size()) { + return switch (args.size()) { case 1 -> { int lowerUpperRange = Integer.parseInt(args.get(0)); - return Range.closed(lowerUpperRange, lowerUpperRange); + yield Range.closed(lowerUpperRange, lowerUpperRange); } case 2 -> { int lower = Integer.parseInt(args.get(0)); int higher = Integer.parseInt(args.get(1)); - return Range.closed(lower, higher); + yield Range.closed(lower, higher); } default -> throw new IllegalArgumentException(range); - } + }; } /** @@ -172,18 +172,18 @@ private static void parseRangeSet(RangeSet result, String ranges) { */ private static Range parseRange(String arg) { List args = COLON_SPLITTER.splitToList(arg); - switch (args.size()) { + return switch (args.size()) { case 1 -> { int line = Integer.parseInt(args.get(0)) - 1; - return Range.closedOpen(line, line + 1); + yield Range.closedOpen(line, line + 1); } case 2 -> { int line0 = Integer.parseInt(args.get(0)) - 1; int line1 = Integer.parseInt(args.get(1)) - 1; - return Range.closedOpen(line0, line1 + 1); + yield Range.closedOpen(line0, line1 + 1); } default -> throw new IllegalArgumentException(arg); - } + }; } /** diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java b/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java index 965be309f..dba0d5582 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java @@ -102,22 +102,18 @@ private static Iterable> reorderBySourcePosition(DequeGiven {@code int @A @B [][] @C []}, adds {@code [[@A, @B], [@C]]} to dims and returns {@code int}. */ private static Tree extractDims(Deque> dims, Tree node) { - switch (node.getKind()) { - case ARRAY_TYPE -> { - return extractDims(dims, ((ArrayTypeTree) node).getType()); - } + return switch (node.getKind()) { + case ARRAY_TYPE -> extractDims(dims, ((ArrayTypeTree) node).getType()); case ANNOTATED_TYPE -> { AnnotatedTypeTree annotatedTypeTree = (AnnotatedTypeTree) node; if (!(annotatedTypeTree.getUnderlyingType() instanceof ArrayTypeTree)) { - return node; + yield node; } node = extractDims(dims, annotatedTypeTree.getUnderlyingType()); dims.addFirst(ImmutableList.copyOf(annotatedTypeTree.getAnnotations())); - return node; - } - default -> { - return node; + yield node; } - } + default -> node; + }; } } From b24961cb2094eb100d09e54c778538187a9ccc7e Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:13:21 +0300 Subject: [PATCH 05/23] Make FormatStringConcatenation an error Eight calls in the tests built a message or an expected string by concatenation and handed it to a method that formats one itself: AssertJ's describedAs, hasMessage and isEqualTo(String, Object...) and Guava's checkState. They now pass a format string and its arguments. --- .../open-java-format.error-prone-conventions.gradle | 1 + .../com/palantir/javaformat/gradle/FormatDiffTest.java | 2 +- .../intellij/PalantirJavaFormatConfigurableTest.java | 4 ++-- .../javaformat/java/FormatterIntegrationTest.java | 8 ++++---- .../palantir/javaformat/java/JavaCommentsHelperTest.java | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index cb7504a5c..324a5a12f 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -55,6 +55,7 @@ tasks.withType(JavaCompile).configureEach { // Checks whose findings have been fixed, build-breaking so that new ones are fixed too rather than read past. tasks.withType(JavaCompile).configureEach { options.errorprone { + error('FormatStringConcatenation') error('RefactorSwitch') error('SameNameButDifferent') error('UnusedVariable') diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java index ba6e08ea5..406baf76d 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java @@ -104,7 +104,7 @@ private void runCommandInRepo(String... args) throws IOException, InterruptedExc process.getErrorStream().transferTo(baos); String stderr = baos.toString(UTF_8); - Preconditions.checkState(process.exitValue() == 0, "Expected return code of 0: " + stderr); + Preconditions.checkState(process.exitValue() == 0, "Expected return code of 0: %s", stderr); } private static Stream getFormatters() throws IOException { diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java index 177201dc5..9728965ac 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java @@ -176,7 +176,7 @@ void showsTheActiveFormatterAsThePageStands() throws Throwable { useNativeImage.setSelected(true); assertThat(activeFormatter().getText()) - .isEqualTo("Need to download version " + pluginVersion + " from Maven Central"); + .isEqualTo("Need to download version %s from Maven Central", pluginVersion); path.setText("/opt/open-java-format"); assertThat(activeFormatter().getText()).isEqualTo("/opt/open-java-format"); @@ -268,7 +268,7 @@ void refusesAPathThatIsNotAnExecutableFile() throws Throwable { assertThatThrownBy(configurable::apply) .isInstanceOf(ConfigurationException.class) - .hasMessage(missing + " is not an executable file"); + .hasMessage("%s is not an executable file", missing); }); } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java index ae644be92..efa7b6288 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java @@ -79,7 +79,7 @@ public void format() { tests.writeFormatterOutput(name, output); return; } - assertThat(output).describedAs("bad output for " + name).isEqualTo(expected); + assertThat(output).describedAs("bad output for %s", name).isEqualTo(expected); } catch (FormatterException e) { throw new RuntimeException(String.format("Formatter crashed on %s", name), e); } @@ -100,7 +100,7 @@ public void idempotentLF() { try { String mangled = expected.replace(separator, "\n"); String output = createFormatter().formatSource(mangled); - assertThat(output).describedAs("bad output for " + name).isEqualTo(mangled); + assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { throw new RuntimeException(String.format("Formatter crashed on %s", name), e); } @@ -113,7 +113,7 @@ public void idempotentCR() { try { String mangled = expected.replace(separator, "\r"); String output = createFormatter().formatSource(mangled); - assertThat(output).describedAs("bad output for " + name).isEqualTo(mangled); + assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { throw new RuntimeException(String.format("Formatter crashed on %s", name), e); } @@ -126,7 +126,7 @@ public void idempotentCRLF() { try { String mangled = expected.replace(separator, "\r\n"); String output = createFormatter().formatSource(mangled); - assertThat(output).describedAs("bad output for " + name).isEqualTo(mangled); + assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { throw new RuntimeException(String.format("Formatter crashed on %s", name), e); } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java index 59e564baf..02a6df3ac 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java @@ -102,7 +102,7 @@ void theMissingSpaceRuleAppliesToEveryLineOfTheRun() throws Exception { // over as one tok (23 and later) or as one tok per line. String rewritten = rewrite("///Summary line.\n" + " ".repeat(COLUMN) + "///More text.", Style.OJF); - assertThat(rewritten).isEqualTo("/// Summary line.\n" + " ".repeat(COLUMN) + "/// More text."); + assertThat(rewritten).isEqualTo("/// Summary line.\n%s/// More text.", " ".repeat(COLUMN)); } @Test From e890042185c24a37ca1622c886fde7fe1cb6107d Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:27:30 +0300 Subject: [PATCH 06/23] Set Error Prone's options in one block The checks that fail the build join excludedPaths in a single block, in alphabetical order. The checks that were only listed as comments for later are gone; the findings still to fix are in the build output. --- ...java-format.error-prone-conventions.gradle | 40 +++---------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 324a5a12f..5d3f6595a 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -18,6 +18,12 @@ tasks.withType(JavaCompile).configureEach { options.errorprone { // Annotation processor output (Immutables) is not ours to fix. excludedPaths = '.*/build/generated/.*' + error('FormatStringConcatenation') + error('JUnitClassModifiers') + error('JUnitMethodDeclaration') + error('RefactorSwitch') + error('SameNameButDifferent') + error('UnusedVariable') } } @@ -27,37 +33,3 @@ pluginManager.withPlugin('java-gradle-plugin') { options.errorprone.disable('JavaxInjectOnAbstractMethod') } } - -// The checks vfs-s3 makes build-breaking. The commented ones stay off until the code is fixed, one check at a -// time: StaticImport 84, LexicographicalAnnotationListing 46, RedundantStringConversion 6, AlmostJavadoc 3 -// findings; the rest have none. -tasks.withType(JavaCompile).configureEach { - options.errorprone { - //error('StaticImport') - //error('UnnecessarilyFullyQualified') - //error('EmptyBlockTag') - //error('OperatorPrecedence') - //error('RedundantStringConversion') - //error('PatternMatchingInstanceof') - //error('UnnecessaryParentheses') - //error('LexicographicalAnnotationListing') - error('JUnitMethodDeclaration') - error('JUnitClassModifiers') - //error('MissingSummary') - //error('AlmostJavadoc') - //error('BadImport') - //error('JdkObsolete') - // NonStaticImport (Picnic) is the inverse of StaticImport and would give contradictory advice. - //disable('NonStaticImport') - } -} - -// Checks whose findings have been fixed, build-breaking so that new ones are fixed too rather than read past. -tasks.withType(JavaCompile).configureEach { - options.errorprone { - error('FormatStringConcatenation') - error('RefactorSwitch') - error('SameNameButDifferent') - error('UnusedVariable') - } -} From 0224ad971a19e45adbb028b6cb4e20fd7e9f80e7 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:29:35 +0300 Subject: [PATCH 07/23] Rename the IntelliJ settings' State class to StoredSettings State also named IntelliJ's @State annotation on the same class, which the previous commit wrote out in full to tell the two apart. The class gets a name of its own and the annotation its import back. The class name is not in .idea/open-java-format.xml: IntelliJ writes the bean's fields into the component element, and PalantirJavaFormatSettingsTest loads files written before. --- .../intellij/PalantirJavaFormatSettings.java | 13 +++++++------ .../javaformat/intellij/FormatterProviderTest.java | 4 ++-- .../PalantirJavaFormatConfigurableTest.java | 6 +++--- .../PalantirJavaFormatFormattingServiceTest.java | 4 ++-- .../intellij/PalantirJavaFormatSettingsTest.java | 10 +++++----- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index c5cadcfdc..83ea2e716 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -18,6 +18,7 @@ import com.intellij.formatting.service.FormattingService; import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; @@ -36,12 +37,12 @@ import static java.util.Optional.ofNullable; -@com.intellij.openapi.components.State( +@State( name = "PalantirJavaFormatSettings", storages = {@Storage("open-java-format.xml")}) -public class PalantirJavaFormatSettings implements PersistentStateComponent { +public class PalantirJavaFormatSettings implements PersistentStateComponent { - private State state = new State(); + private StoredSettings state = new StoredSettings(); static PalantirJavaFormatSettings getInstance(Project project) { return project.getService(PalantirJavaFormatSettings.class); @@ -49,12 +50,12 @@ static PalantirJavaFormatSettings getInstance(Project project) { @Nullable @Override - public State getState() { + public StoredSettings getState() { return state; } @Override - public void loadState(State state) { + public void loadState(StoredSettings state) { this.state = state; } @@ -148,7 +149,7 @@ enum EnabledState { DISABLED } - static class State { + static class StoredSettings { private EnabledState enabled = EnabledState.UNKNOWN; private Optional> implementationClassPath = Optional.empty(); diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/FormatterProviderTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/FormatterProviderTest.java index 0bfa7dbd6..1dc3d138b 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/FormatterProviderTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/FormatterProviderTest.java @@ -22,7 +22,7 @@ import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.palantir.javaformat.bootstrap.BootstrappingFormatterService; import com.palantir.javaformat.bootstrap.NativeImageFormatterService; -import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; import java.nio.file.Path; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -45,7 +45,7 @@ void setUp() throws Exception { fixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture()); fixture.setUp(); settings = PalantirJavaFormatSettings.getInstance(fixture.getProject()); - settings.loadState(new State()); + settings.loadState(new StoredSettings()); } @AfterEach diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java index 9728965ac..c74d18bf6 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java @@ -34,7 +34,7 @@ import com.palantir.javaformat.intellij.EmbeddedNativeImage.Installed; import com.palantir.javaformat.intellij.EmbeddedNativeImage.Status; import com.palantir.javaformat.intellij.PalantirJavaFormatConfigurable.StatusLine; -import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; import java.awt.Component; import java.awt.Container; import java.io.IOException; @@ -74,7 +74,7 @@ void setUp() throws Exception { fixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture()); fixture.setUp(); settings = PalantirJavaFormatSettings.getInstance(fixture.getProject()); - settings.loadState(new State()); + settings.loadState(new StoredSettings()); settings.setEnabled(true); // Nothing downloaded, and nowhere to download from. var embedded = new EmbeddedNativeImage( @@ -104,7 +104,7 @@ void hasNoChoiceOfCodeStyle() { @Test void readsAndWritesTheEnabledSetting() throws Throwable { - State disabled = new State(); + var disabled = new StoredSettings(); disabled.setEnabled("false"); settings.loadState(disabled); diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 4a039bab0..2401d64e6 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -35,7 +35,7 @@ import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; -import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -70,7 +70,7 @@ void setUp() throws Exception { settings = PalantirJavaFormatSettings.getInstance(fixture.getProject()); - State resetState = new State(); + var resetState = new StoredSettings(); resetState.setEnabled("true"); settings.loadState(resetState); } diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java index d3d574b4b..8596c2e1f 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java @@ -18,7 +18,7 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.util.xmlb.XmlSerializer; -import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; import java.nio.file.Path; import java.util.Optional; import org.intellij.lang.annotations.Language; @@ -42,7 +42,7 @@ void loadsAFileThatStillHasTheCodeStyle() throws Exception { @Test void doesNotWriteACodeStyle() { - var xml = JDOMUtil.write(XmlSerializer.serialize(new State())); + var xml = JDOMUtil.write(XmlSerializer.serialize(new StoredSettings())); assertThat(xml).doesNotContain("style"); } @@ -78,7 +78,7 @@ void ignoresTheNativeImageTheGradlePluginUsedToWrite() throws Exception { @Test void takesABlankPathForNone() { - var state = new State(); + var state = new StoredSettings(); state.nativeImagePath = " "; var settings = new PalantirJavaFormatSettings(); settings.loadState(state); @@ -86,7 +86,7 @@ void takesABlankPathForNone() { assertThat(settings.getNativeImagePath()).isEmpty(); } - private static State deserialize(@Language("xml") String xml) throws Exception { - return XmlSerializer.deserialize(JDOMUtil.load(xml), State.class); + private static StoredSettings deserialize(@Language("xml") String xml) throws Exception { + return XmlSerializer.deserialize(JDOMUtil.load(xml), StoredSettings.class); } } From a89db5b0dd75bd1c6ed3816371d0141022894a2b Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 16:29:35 +0300 Subject: [PATCH 08/23] Spell out the expected text in theMissingSpaceRuleAppliesToEveryLineOfTheRun As a format string the expected comment no longer read like the input it is rewritten from. The concatenation is back, with FormatStringConcatenation suppressed on the test. --- .../com/palantir/javaformat/java/JavaCommentsHelperTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java index 02a6df3ac..f5d2202a2 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java @@ -96,13 +96,14 @@ void anUnbreakableTokenIsLeftLong() throws Exception { assertThat(rewritten).contains("/// " + url); } + @SuppressWarnings("FormatStringConcatenation") // Written out like the input, not as a template @Test void theMissingSpaceRuleAppliesToEveryLineOfTheRun() throws Exception { // Otherwise the same source formats differently depending on whether the running JDK hands the run // over as one tok (23 and later) or as one tok per line. String rewritten = rewrite("///Summary line.\n" + " ".repeat(COLUMN) + "///More text.", Style.OJF); - assertThat(rewritten).isEqualTo("/// Summary line.\n%s/// More text.", " ".repeat(COLUMN)); + assertThat(rewritten).isEqualTo("/// Summary line.\n" + " ".repeat(COLUMN) + "/// More text."); } @Test From 3e19dc858a733c63c75b8cec277c5b2449e079cf Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 17:08:14 +0300 Subject: [PATCH 09/23] Read Signature.bundled instead of suppressing UnusedVariable The 21.X.X merge brought in Signature, whose bundled field only went into the serialized signature and carried a suppression. jars() now picks the jars by it: the hash is there exactly when the build declared none of its own, so the step formats with the same jars as before. --- .../javaformat/gradle/spotless/PalantirJavaFormatStep.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java index d56ecbea1..9cff43b4d 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/spotless/PalantirJavaFormatStep.java @@ -84,8 +84,7 @@ Signature sign() throws IOException { static final class Signature implements Serializable { private static final long serialVersionUID = 1L; - // Spotless compares the signature in its serialized form, and reads nothing out of it. - @SuppressWarnings("UnusedVariable") + /** The hash of the plugin's own jars, or empty when the build declared its own. */ private final String bundled; private final FileSignature override; @@ -100,7 +99,7 @@ static final class Signature implements Serializable { } List jars() { - return BundledFormatter.jarsUnlessOverridden(override.files(), gradleUserHome); + return bundled.isEmpty() ? List.copyOf(override.files()) : BundledFormatter.jars(gradleUserHome); } } From 0c5bc4a66fd833769ab04e4cdc0f343e03592766 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 17:58:15 +0300 Subject: [PATCH 10/23] Use JSpecify's @Nullable, and no other nullness annotation jsr305's javax.annotation.Nullable gives way to org.jspecify.annotations.Nullable, and the jsr305 dependency to jspecify 1.0.1, the version Guava already brings. Obs's @CheckReturnValue, the other jsr305 annotation in use, is now Error Prone's. JetBrains' @NotNull goes from the IntelliJ plugin: what is not @Nullable is non-null, and NullAway checks it. The plugin does not instrument its code, so the annotations added no runtime checks either. JSpecify's @Nullable is a type-use annotation. Where it stood above an @Override it now follows it, next to the return type it annotates. CLAUDE.md names the annotation and where it goes on nested and array types. --- CLAUDE.md | 6 ++++-- gradle/libs.versions.toml | 2 +- open-java-format-eclipse-plugin/build.gradle | 2 +- .../javaformat/java/PalantirJavaFormatter.java | 6 +++--- open-java-format-idea-plugin/build.gradle | 2 +- .../javaformat/intellij/EmbeddedNativeImage.java | 2 +- .../InitialConfigurationStartupActivity.java | 3 +-- .../NativeImageNoticeStartupActivity.java | 5 ++--- .../intellij/PalantirJavaFormatConfigurable.java | 10 ++++------ .../PalantirJavaFormatFormattingService.java | 15 +++++++-------- .../intellij/PalantirJavaFormatSettings.java | 4 ++-- .../PalantirJavaFormatFormattingServiceTest.java | 2 -- open-java-format/build.gradle | 2 +- .../java/com/palantir/javaformat/doc/Level.java | 2 +- .../java/com/palantir/javaformat/doc/Obs.java | 2 +- .../javaformat/java/CommandLineOptionsParser.java | 2 +- .../javaformat/java/InputMetadataBuilder.java | 2 +- .../javaformat/java/JavaCommentsHelper.java | 2 +- .../javaformat/java/JavaInputAstVisitor.java | 2 +- .../palantir/javaformat/java/ModifierOrderer.java | 2 +- .../palantir/javaformat/java/StringWrapper.java | 2 +- .../java/com/palantir/javaformat/java/Trees.java | 2 +- .../palantir/javaformat/java/UsageException.java | 2 +- 23 files changed, 38 insertions(+), 43 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 695521c1e..3ed0f72fc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,8 +8,10 @@ Building and testing: the README's "Building and testing locally" section. Gradl - **Collections.** Plain `java.util` types. `List.of`, `List.copyOf`, `Map.of` and `Stream.toList()` give unmodifiable collections, so new code does not need Guava's `ImmutableList` or `ImmutableMap`; use them only where an existing API already hands one over. -- **Nullability.** One annotation, `javax.annotation.Nullable` (jsr305), the one the code uses; do not add another - `Nullable`. +- **Nullability.** One annotation, JSpecify's `org.jspecify.annotations.Nullable`; do not add another `Nullable`, + nor a `NotNull` or `NonNull`: what is not `@Nullable` is non-null, and NullAway checks it. It is a type-use + annotation, so on a nested type it goes before the simple name (`Input.@Nullable Token`), and on an array before + the brackets (`String @Nullable []`). - **Local variables.** `var` wherever the initializer gives the type unchanged, in `for` loops too. Not where it would box a primitive (`for (var n : List)`), and not without an initializer that has a type of its own (a lambda, `null`). diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 95dc01d2d..e41b0d3bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ jackson-datatype-jdk8 = { module = "com.fasterxml.jackson.datatype:jackson-datat jackson-module-parameterNames = { module = "com.fasterxml.jackson.module:jackson-module-parameter-names", version = "2.22.4" } jmh-core = { module = "org.openjdk.jmh:jmh-core", version = "1.37" } jmh-generator-annprocess = { module = "org.openjdk.jmh:jmh-generator-annprocess", version = "1.37" } -jsr305 = { module = "com.google.code.findbugs:jsr305", version = "3.0.2" } +jspecify = { module = "org.jspecify:jspecify", version = "1.0.1" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version = "6.1.3" } junit-jupiter-migrationsupport = { module = "org.junit.jupiter:junit-jupiter-migrationsupport", version = "6.1.3" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version = "6.1.3" } diff --git a/open-java-format-eclipse-plugin/build.gradle b/open-java-format-eclipse-plugin/build.gradle index b3bc9cce9..8e4deca32 100644 --- a/open-java-format-eclipse-plugin/build.gradle +++ b/open-java-format-eclipse-plugin/build.gradle @@ -28,7 +28,7 @@ dependencies { compileOnly libs.eclipse.text implementation project(':open-java-format') - implementation libs.jsr305 + implementation libs.jspecify } // The dependencies embedded in the plugin jar. Named once, so the manifest can list exactly what diff --git a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/PalantirJavaFormatter.java b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/PalantirJavaFormatter.java index 94d6cfe9e..8e9e1b262 100644 --- a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/PalantirJavaFormatter.java +++ b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/PalantirJavaFormatter.java @@ -19,7 +19,6 @@ import com.palantir.javaformat.java.SnippetFormatter.SnippetKind; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jface.text.IRegion; @@ -27,22 +26,23 @@ import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; +import org.jspecify.annotations.Nullable; /** Runs the Palantir Java formatter on the given code. */ public class PalantirJavaFormatter extends CodeFormatter { private static final int INDENTATION_SIZE = 4; - @Nullable @Override + @Nullable public TextEdit format( int kind, String source, int offset, int length, int indentationLevel, String _lineSeparator) { IRegion[] regions = new IRegion[] {new Region(offset, length)}; return formatInternal(kind, source, regions, indentationLevel); } - @Nullable @Override + @Nullable public TextEdit format(int kind, String source, IRegion[] regions, int indentationLevel, String _lineSeparator) { return formatInternal(kind, source, regions, indentationLevel); } diff --git a/open-java-format-idea-plugin/build.gradle b/open-java-format-idea-plugin/build.gradle index f0e43a8ac..9796fb5ab 100644 --- a/open-java-format-idea-plugin/build.gradle +++ b/open-java-format-idea-plugin/build.gradle @@ -81,7 +81,7 @@ dependencies { testImplementation libs.assertj testImplementation libs.junit.jupiter testImplementation libs.junit.platform.launcher - implementation libs.jsr305 + implementation libs.jspecify } intellijPlatform { diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/EmbeddedNativeImage.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/EmbeddedNativeImage.java index cf2abc701..7fb8b33cd 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/EmbeddedNativeImage.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/EmbeddedNativeImage.java @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** * The native binary the plugin downloads for the IDE's platform, one for the whole IDE. It is kept in the IDE's system diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/InitialConfigurationStartupActivity.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/InitialConfigurationStartupActivity.java index 941d1603f..b4c3261fb 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/InitialConfigurationStartupActivity.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/InitialConfigurationStartupActivity.java @@ -18,11 +18,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupActivity; -import org.jetbrains.annotations.NotNull; public final class InitialConfigurationStartupActivity implements StartupActivity.DumbAware { @Override - public void runActivity(@NotNull Project project) { + public void runActivity(Project project) { PalantirJavaFormatSettings settings = PalantirJavaFormatSettings.getInstance(project); if (settings.isUninitialized()) { settings.setEnabled(false); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/NativeImageNoticeStartupActivity.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/NativeImageNoticeStartupActivity.java index 8f5fd78a6..cf44f4495 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/NativeImageNoticeStartupActivity.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/NativeImageNoticeStartupActivity.java @@ -22,8 +22,7 @@ import com.intellij.openapi.startup.StartupActivity; import com.palantir.javaformat.intellij.EmbeddedNativeImage.Status; import java.util.Optional; -import javax.annotation.Nullable; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; /** * Tells the user, as the IDE does about outdated spelling dictionaries, when the embedded native binary is older than @@ -41,7 +40,7 @@ record Missing() implements Notice {} } @Override - public void runActivity(@NotNull Project project) { + public void runActivity(Project project) { var embedded = EmbeddedNativeImage.getInstance(); embedded.removeStale(); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurable.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurable.java index 6f1f7238b..0b5dc9d97 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurable.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurable.java @@ -41,7 +41,6 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.Optional; -import javax.annotation.Nullable; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -50,7 +49,7 @@ import javax.swing.JTextArea; import javax.swing.event.DocumentEvent; import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; class PalantirJavaFormatConfigurable extends BaseConfigurable implements SearchableConfigurable { @@ -130,14 +129,13 @@ protected void textChanged(DocumentEvent event) { .getPanel(); } - @NotNull @Override public String getId() { return "open-java-format.settings"; } - @Nullable @Override + @Nullable public Runnable enableSearch(String option) { return null; } @@ -148,14 +146,14 @@ public String getDisplayName() { return "open-java-format Settings"; } - @Nullable @Override + @Nullable public String getHelpTopic() { return null; } - @Nullable @Override + @Nullable public JComponent createComponent() { if (uiDisposable == null) { uiDisposable = Disposer.newDisposable("open-java-format settings page"); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java index 58d53559f..90f5d5364 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java @@ -33,9 +33,8 @@ import com.palantir.javaformat.java.FormatterException; import com.palantir.javaformat.java.FormatterService; import com.palantir.javaformat.java.Replacement; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -57,7 +56,7 @@ class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService private PluginDescriptor pluginDescriptor; @Override - public void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) { + public void setPluginDescriptor(PluginDescriptor pluginDescriptor) { this.pluginDescriptor = pluginDescriptor; } @@ -66,7 +65,7 @@ Optional getPluginDescriptor() { } @Override - protected FormattingTask createFormattingTask(@NotNull AsyncFormattingRequest request) { + protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { Project project = request.getContext().getProject(); // The marketplace verifier reports this class as experimental, and newer IDEs also deprecate it. It stays: // its replacement, com.intellij.ide.trustedProjects.TrustedProjects, is @ApiStatus.Internal — a worse thing @@ -84,22 +83,22 @@ protected FormattingTask createFormattingTask(@NotNull AsyncFormattingRequest re } @Override - protected @NotNull String getNotificationGroupId() { + protected String getNotificationGroupId() { return Notifications.PARSING_ERROR_NOTIFICATION_GROUP; } @Override - protected @NotNull @NlsSafe String getName() { + protected @NlsSafe String getName() { return "open-java-format"; } @Override - public @NotNull Set getFeatures() { + public Set getFeatures() { return Set.of(Feature.FORMAT_FRAGMENTS); } @Override - public boolean canFormat(@NotNull PsiFile file) { + public boolean canFormat(PsiFile file) { return JavaFileType.INSTANCE.equals(file.getFileType()) && PalantirJavaFormatSettings.getInstance(file.getProject()).isEnabled(); } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 83ea2e716..9808a888f 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -23,8 +23,8 @@ import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; import com.palantir.javaformat.java.FormatterService; +import org.jspecify.annotations.Nullable; -import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -48,8 +48,8 @@ static PalantirJavaFormatSettings getInstance(Project project) { return project.getService(PalantirJavaFormatSettings.class); } - @Nullable @Override + @Nullable public StoredSettings getState() { return state; } diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 2401d64e6..ab39e5dd7 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -36,7 +36,6 @@ import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -102,7 +101,6 @@ protected Project getProject() { return fixture.getProject(); } - @NotNull protected LightProjectDescriptor getProjectDescriptor() { return new DefaultLightProjectDescriptor() { @Override diff --git a/open-java-format/build.gradle b/open-java-format/build.gradle index b16e08fac..016e7fa55 100644 --- a/open-java-format/build.gradle +++ b/open-java-format/build.gradle @@ -26,7 +26,7 @@ dependencies { compileOnly variantOf(libs.immutables.value) { classifier('annotations') } annotationProcessor libs.immutables.value - implementation libs.jsr305 + implementation libs.jspecify } // Was baseline's `moduleJvmArgs`, dropped because it looks its extension up at execution diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java index ce78867d6..176616b06 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Level.java @@ -48,7 +48,7 @@ import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** A {@code Level} inside a {@link Doc}. */ public final class Level extends Doc { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java index 0fcbf7ede..fff15b482 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java @@ -16,10 +16,10 @@ package com.palantir.javaformat.doc; +import com.google.errorprone.annotations.CheckReturnValue; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Function; -import javax.annotation.CheckReturnValue; /** * These classes exist purely for observing the operation of {@link Doc#computeBreaks}, including all the alternative diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java index 06088b223..4ed307c9e 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** A parser for {@link CommandLineOptions}. */ final class CommandLineOptionsParser { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/InputMetadataBuilder.java b/open-java-format/src/main/java/com/palantir/javaformat/java/InputMetadataBuilder.java index 7bc9846d4..6ded45a09 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/InputMetadataBuilder.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/InputMetadataBuilder.java @@ -24,7 +24,7 @@ import com.palantir.javaformat.OpsBuilder.BlankLineWanted; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; public final class InputMetadataBuilder { private final Map blankLines = new HashMap<>(); // Info on blank lines. diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaCommentsHelper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaCommentsHelper.java index 80193bbbe..1a2de78d9 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaCommentsHelper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaCommentsHelper.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** {@code JavaCommentsHelper} extends {@link CommentsHelper} to rewrite Java comments. */ public final class JavaCommentsHelper implements CommentsHelper { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index 2c374113b..9c758a7f1 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -146,9 +146,9 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Stream; -import javax.annotation.Nullable; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; +import org.jspecify.annotations.Nullable; /** An AST visitor that builds a stream of {@link Op}s to format from the given {@link CompilationUnitTree}. */ public class JavaInputAstVisitor extends TreePathScanner { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java index 3fa532e6e..c63963112 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java @@ -33,8 +33,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; import javax.lang.model.element.Modifier; +import org.jspecify.annotations.Nullable; /** Fixes sequences of modifiers to be in JLS order. */ final class ModifierOrderer { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index 6034b35a1..74fe6d190 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -51,7 +51,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** Wraps string literals that exceed the column limit, and joins adjacent ones that share a line. */ public final class StringWrapper { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java index 9d573102a..2eba020ef 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java @@ -35,8 +35,8 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; -import javax.annotation.Nullable; import javax.lang.model.element.Name; +import org.jspecify.annotations.Nullable; /** Utilities for working with {@link Tree}s. */ class Trees { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/UsageException.java b/open-java-format/src/main/java/com/palantir/javaformat/java/UsageException.java index eca7a781e..c8644ecc5 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/UsageException.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/UsageException.java @@ -17,7 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Joiner; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** Checked exception class for formatter command-line usage errors. */ final class UsageException extends Exception { From 955f67953a1faa40e66e06d8ea29ced7417b637c Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 17:58:16 +0300 Subject: [PATCH 11/23] Run NullAway in JSpecify mode Outside it NullAway takes Guava's @ParametricNullness for @Nullable: PeekingIterator.peek() and next(), Iterables.getLast and getOnlyElement, Iterators.getLast. Every token taken from a PeekingIterator was reported as possibly null. JSpecify mode reads Guava's type parameters instead, and of NullAway's 102 findings 60 remain. On javac 21 the mode needs -XDaddTypeAnnotationsToSymbol=true to see type-use annotations, @Nullable among them, on classes read from jars. --- .../groovy/open-java-format.error-prone-conventions.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 5d3f6595a..97dc0b537 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -12,9 +12,12 @@ dependencies { nullaway { annotatedPackages.add('com.palantir') + jspecifyMode = true } tasks.withType(JavaCompile).configureEach { + // JSpecify mode reads @Nullable on types from class files, which javac 21 does only with this flag. + options.compilerArgs.add('-XDaddTypeAnnotationsToSymbol=true') options.errorprone { // Annotation processor output (Immutables) is not ours to fix. excludedPaths = '.*/build/generated/.*' From 1d03bf95fdeec3d34cabed0ac3eb73c81ba2ce9c Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 17:59:14 +0300 Subject: [PATCH 12/23] Turn off LexicographicalAnnotationListing Picnic's check asks for annotations in alphabetical order, and gave 25 suggestions that would move @Override and @Test below the rest. The annotation that says what a member is stays first, as most of the code has it. JavaCommentsHelperTest's @Test is above its @SuppressWarnings again. --- .../main/groovy/open-java-format.error-prone-conventions.gradle | 1 + .../com/palantir/javaformat/java/JavaCommentsHelperTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 97dc0b537..2e474d4ae 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -27,6 +27,7 @@ tasks.withType(JavaCompile).configureEach { error('RefactorSwitch') error('SameNameButDifferent') error('UnusedVariable') + disable('LexicographicalAnnotationListing') } } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java index f5d2202a2..cf25955c4 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/JavaCommentsHelperTest.java @@ -96,8 +96,8 @@ void anUnbreakableTokenIsLeftLong() throws Exception { assertThat(rewritten).contains("/// " + url); } - @SuppressWarnings("FormatStringConcatenation") // Written out like the input, not as a template @Test + @SuppressWarnings("FormatStringConcatenation") // Written out like the input, not as a template void theMissingSpaceRuleAppliesToEveryLineOfTheRun() throws Exception { // Otherwise the same source formats differently depending on whether the running JDK hands the run // over as one tok (23 and later) or as one tok per line. From ab27a785e8df07c467b3e73364e719b306f0ae31 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:08:16 +0300 Subject: [PATCH 13/23] Fix what NullAway reports Most of the 60 findings were a null the code meant, with no @Nullable where it went. Now marked @Nullable: - RawTok's and Tok's kind and string value, and a comment's text before it is first read; - the parent of the root exploration node; - the methods maybeGetMethod looks up, and the fields that keep them; - the receiver of an unqualified call, and a dimension list's missing type; - getLineEnding's result for a string without one; - the three scan overrides, which javac calls with null for a missing tree. Where a lookup cannot miss (a token at a position the input has, a range the output built, a level's children), checkNotNull says so and fails as the dereference did. Besides that: - variableFragmentDims no longer takes a missing type: its one caller that may have none maps the Optional instead. - The javadoc lexer's newline after a `}` it moves has the "\n" of every other newline token rather than null. The formatter writes a line break for it and never reads the value. - Newlines.LineIterator returns the line it reads instead of keeping it in a field. - BootstrappingFormatterService runs the formatter without a working directory, rather than throwing, if the java binary has no parent directory. - PalantirJavaFormatSettings.getState() is no longer @Nullable: it never returns null. - ParameterizedClass uses JUnit 6's computeIfAbsent, which never returns null, in place of the deprecated getOrComputeIfAbsent. - FormatterIntegrationTest reads the expected output's line separator in the tests that use it. - NullAway takes AssertJ's assertThat(x).isNotNull() in tests for the null check it is. --- ...java-format.error-prone-conventions.gradle | 3 +++ .../gradle/NativeImageAtomicCopyTest.java | 6 +++-- .../PalantirJavaFormatFormattingService.java | 1 + .../intellij/PalantirJavaFormatSettings.java | 3 ++- ...lantirJavaFormatFormattingServiceTest.java | 11 ++++---- .../BootstrappingFormatterService.java | 2 +- .../com/palantir/javaformat/Newlines.java | 10 ++++---- .../com/palantir/javaformat/OpsBuilder.java | 8 +++--- .../java/com/palantir/javaformat/doc/Obs.java | 3 ++- .../java/CommandLineOptionsParser.java | 2 +- .../javaformat/java/DimensionHelpers.java | 5 +++- .../palantir/javaformat/java/JavaInput.java | 18 ++++++++++--- .../javaformat/java/JavaInputAstVisitor.java | 25 ++++++++++--------- .../palantir/javaformat/java/JavaOutput.java | 10 +++++--- .../palantir/javaformat/java/JavacTokens.java | 10 +++++++- .../palantir/javaformat/java/JsonSink.java | 8 ++++-- .../javaformat/java/ModifierOrderer.java | 4 ++- .../javaformat/java/RemoveUnusedImports.java | 14 +++++++---- .../com/palantir/javaformat/java/Trees.java | 1 + .../java/java14/Java14InputAstVisitor.java | 15 ++++++++++- .../java/java21/Java21InputAstVisitor.java | 3 ++- .../javaformat/java/javadoc/JavadocLexer.java | 2 +- .../java/FormatterIntegrationTest.java | 14 +++++++---- .../javaformat/java/FormatterTest.java | 4 ++- .../jupiter/ParameterizedClass.java | 4 +-- 25 files changed, 127 insertions(+), 59 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 2e474d4ae..3388b9ce8 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -21,6 +21,9 @@ tasks.withType(JavaCompile).configureEach { options.errorprone { // Annotation processor output (Immutables) is not ours to fix. excludedPaths = '.*/build/generated/.*' + nullaway { + handleTestAssertionLibraries = true + } error('FormatStringConcatenation') error('JUnitClassModifiers') error('JUnitMethodDeclaration') diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageAtomicCopyTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageAtomicCopyTest.java index e5e56d523..1e3f04d3e 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageAtomicCopyTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageAtomicCopyTest.java @@ -45,8 +45,10 @@ void the_copy_is_executable_and_the_image_is_left_as_it_was() throws IOException var copy = NativeImageAtomicCopy.copyToCache(image, gradleUserHome.toFile()); - assertThat(copy.getParent().getParent()).isEqualTo(gradleUserHome.resolve("open-java-format-caches")); - assertThat(copy.getParent().getFileName().toString()).matches("[0-9a-f]{16}"); + var cacheDir = copy.getParent(); + assertThat(cacheDir).isNotNull(); + assertThat(cacheDir.getParent()).isEqualTo(gradleUserHome.resolve("open-java-format-caches")); + assertThat(cacheDir.getFileName().toString()).matches("[0-9a-f]{16}"); assertThat(copy.getFileName()).isEqualTo(image.getFileName()); assertThat(copy).hasContent("an image"); assertThat(Files.isExecutable(copy)).isTrue(); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java index 90f5d5364..c5b677d1d 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java @@ -65,6 +65,7 @@ Optional getPluginDescriptor() { } @Override + @Nullable protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { Project project = request.getContext().getProject(); // The marketplace verifier reports this class as experimental, and newer IDEs also deprecate it. It stays: diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 9808a888f..ae3ea7bd0 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -49,7 +49,6 @@ static PalantirJavaFormatSettings getInstance(Project project) { } @Override - @Nullable public StoredSettings getState() { return state; } @@ -164,6 +163,7 @@ public void setImplementationClassPath(@Nullable List value) { .map(strings -> strings.stream().map(URI::create).collect(Collectors.toList())); } + @Nullable public List getImplementationClassPath() { return implementationClassPath .map(paths -> paths.stream().map(URI::toString).collect(Collectors.toList())) @@ -181,6 +181,7 @@ public void setEnabled(@Nullable String enabledStr) { } } + @Nullable public String getEnabled() { return switch (enabled) { case ENABLED -> "true"; diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index ab39e5dd7..111e491ea 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -48,6 +48,7 @@ import java.nio.file.Paths; import java.util.List; +import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; final class PalantirJavaFormatFormattingServiceTest { @@ -126,10 +127,10 @@ private PsiFile createPsiFile(String path, String... contents) throws IOExceptio private static final class DelegatingFormatter extends PalantirJavaFormatFormattingService { public DelegatingFormatter() { - this.setPluginDescriptor(FormattingService.EP_NAME - .findExtension(PalantirJavaFormatFormattingService.class) - .getPluginDescriptor() - .orElseThrow()); + this.setPluginDescriptor( + requireNonNull(FormattingService.EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) + .getPluginDescriptor() + .orElseThrow()); } private boolean invoked = false; @@ -140,7 +141,7 @@ private boolean wasInvoked() { @Override protected FormattingTask createFormattingTask(AsyncFormattingRequest request) { - FormattingTask delegateTask = super.createFormattingTask(request); + FormattingTask delegateTask = requireNonNull(super.createFormattingTask(request)); return new FormattingTask() { @Override public boolean cancel() { diff --git a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java index f5b13f314..5467b6105 100644 --- a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java +++ b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java @@ -87,7 +87,7 @@ private ImmutableList getFormatReplacementsInternal(String input, C ranges.stream().map(RangeUtils::toStringRange).collect(Collectors.toList())); Optional output = - FormatterCommandRunner.runWithStdin(command.toArgs(), input, Optional.of(jdkPath.getParent())); + FormatterCommandRunner.runWithStdin(command.toArgs(), input, Optional.ofNullable(jdkPath.getParent())); if (output.isEmpty() || output.get().isEmpty()) { return ImmutableList.of(); } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java b/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java index 05bb595d2..4f9eed64b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/Newlines.java @@ -19,6 +19,7 @@ import com.google.common.collect.Iterators; import java.util.Iterator; import java.util.NoSuchElementException; +import org.jspecify.annotations.Nullable; /** Platform-independent newline handling. */ public class Newlines { @@ -53,6 +54,7 @@ public static int hasNewlineAt(String input, int idx) { } /** Returns the terminating line break in the input, or {@code null} if the input does not end in a break. */ + @Nullable public static String getLineEnding(String input) { for (String b : BREAKS) { if (input.endsWith(b)) { @@ -154,7 +156,6 @@ public void remove() { private static final class LineIterator implements Iterator { int idx; - String curr; private final String input; private final Iterator indices; @@ -165,7 +166,7 @@ private LineIterator(String input) { idx = indices.next(); // read leading 0 } - private void advance() { + private String advance() { int last = idx; if (indices.hasNext()) { idx = indices.next(); @@ -175,7 +176,7 @@ private void advance() { } else { throw new NoSuchElementException(); } - curr = input.substring(last, idx); + return input.substring(last, idx); } @Override @@ -185,8 +186,7 @@ public boolean hasNext() { @Override public String next() { - advance(); - return curr; + return advance(); } @Override diff --git a/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java b/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java index 837c9fb87..25234ff67 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/OpsBuilder.java @@ -14,6 +14,8 @@ package com.palantir.javaformat; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; @@ -49,14 +51,14 @@ public final class OpsBuilder { /** Return the actual size of the AST node at position, including comments. */ public int actualSize(int position, int length) { - Input.Token startToken = input.getPositionTokenMap().get(position); + Input.Token startToken = checkNotNull(input.getPositionTokenMap().get(position)); int start = startToken.getTok().getPosition(); for (Input.Tok tok : startToken.getToksBefore()) { if (tok.isComment()) { start = Math.min(start, tok.getPosition()); } } - Input.Token endToken = input.getPositionTokenMap().get(position + length - 1); + Input.Token endToken = checkNotNull(input.getPositionTokenMap().get(position + length - 1)); int end = endToken.getTok().getPosition() + endToken.getTok().length(); for (Input.Tok tok : endToken.getToksAfter()) { if (tok.isComment()) { @@ -68,7 +70,7 @@ public int actualSize(int position, int length) { /** Return the start column of the token at {@code position}, including leading comments. */ public Integer actualStartColumn(int position) { - Input.Token startToken = input.getPositionTokenMap().get(position); + Input.Token startToken = checkNotNull(input.getPositionTokenMap().get(position)); int start = startToken.getTok().getPosition(); int line0 = input.getLineNumber(start); for (Input.Tok tok : startToken.getToksBefore()) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java index fff15b482..ba0b09dc3 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.function.Function; +import org.jspecify.annotations.Nullable; /** * These classes exist purely for observing the operation of {@link Doc#computeBreaks}, including all the alternative @@ -170,7 +171,7 @@ private static class ExplorationNodeImpl extends HasUniqueId implements Explorat private final Optional parentLevel; public ExplorationNodeImpl( - LevelNodeImpl parent, + @Nullable LevelNodeImpl parent, String humanDescription, Sink sink, int startColumn, diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java index 4ed307c9e..658833405 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java @@ -107,7 +107,7 @@ static CommandLineOptions parse(Iterable options) { return optionsBuilder.build(); } - private static Integer parseInteger(Iterator it, String flag, String value) { + private static Integer parseInteger(Iterator it, String flag, @Nullable String value) { try { return Integer.valueOf(getValue(flag, it, value)); } catch (NumberFormatException e) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java b/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java index dba0d5582..3e01962d6 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/DimensionHelpers.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Deque; import java.util.List; +import org.jspecify.annotations.Nullable; /** * Utilities for working with array dimensions. @@ -38,10 +39,12 @@ class DimensionHelpers { /** The array dimension specifiers (including any type annotations) associated with a type. */ static class TypeWithDims { + @Nullable final Tree node; + final ImmutableList> dims; - public TypeWithDims(Tree node, ImmutableList> dims) { + public TypeWithDims(@Nullable Tree node, ImmutableList> dims) { this.node = node; this.dims = dims; } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java index 950ae3ac7..e46b20818 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInput.java @@ -57,6 +57,7 @@ import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.SimpleJavaFileObject; +import org.jspecify.annotations.Nullable; /** {@code JavaInput} extends {@link Input} to represent a Java input document. */ public final class JavaInput extends Input { @@ -79,6 +80,8 @@ static final class Tok implements Input.Tok { private final int position; private final int columnI; private final boolean isToken; + + @Nullable private final TokenKind kind; /** @@ -92,7 +95,14 @@ static final class Tok implements Input.Tok { * @param isToken whether the {@code Tok} is a token * @param kind the token kind */ - Tok(int index, String originalText, String text, int position, int columnI, boolean isToken, TokenKind kind) { + Tok( + int index, + String originalText, + String text, + int position, + int columnI, + boolean isToken, + @Nullable TokenKind kind) { this.index = index; this.originalText = originalText; this.text = text; @@ -174,6 +184,7 @@ public String toString() { .toString(); } + @Nullable public TokenKind kind() { return kind; } @@ -383,7 +394,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept // Get string, possibly with Unicode escapes. String originalTokText = text.substring(charI0, t.endPos()); String tokText = t.kind() == TokenKind.STRINGLITERAL - ? t.stringVal() // Unicode escapes removed. + ? checkNotNull(t.stringVal()) // Unicode escapes removed. : originalTokText; char tokText0 = tokText.charAt(0); // The token's first character. final boolean isToken; // Is this tok a token? @@ -414,7 +425,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept } else if (tokText.startsWith("//") || tokText.startsWith("/*")) { // For compatibility with an earlier lexer, the newline after a // comment is its own tok. if (tokText.startsWith("//") && (originalTokText.endsWith("\n") || originalTokText.endsWith("\r"))) { - extraNewline = Newlines.getLineEnding(originalTokText); + extraNewline = checkNotNull(Newlines.getLineEnding(originalTokText)); tokText = tokText.substring(0, tokText.length() - extraNewline.length()); originalTokText = originalTokText.substring(0, originalTokText.length() - extraNewline.length()); } @@ -707,6 +718,7 @@ public String toString() { .toString(); } + @Nullable private JCCompilationUnit unit; @Override diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index 9c758a7f1..3d68ccc9b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -14,6 +14,7 @@ package com.palantir.javaformat.java; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getOnlyElement; import static com.palantir.javaformat.Indent.If.make; @@ -330,7 +331,7 @@ private boolean inExpression() { } @Override - public Void scan(Tree tree, Void unused) { + public Void scan(@Nullable Tree tree, Void unused) { inExpression.addLast(tree instanceof ExpressionTree || inExpression.peekLast()); int previous = builder.depth(); try { @@ -984,7 +985,7 @@ void visitVariables(List fragments, DeclarationKind declarationKin Optional.ofNullable(fragment.getInitializer()), Optional.of(";"), /* receiverExpression= */ Optional.empty(), - Optional.ofNullable(variableFragmentDims(true, 0, fragment.getType()))); + Optional.ofNullable(fragment.getType()).map(type -> variableFragmentDims(true, 0, type))); } else { declareMany(fragments, annotationDirection); @@ -992,9 +993,6 @@ void visitVariables(List fragments, DeclarationKind declarationKin } private TypeWithDims variableFragmentDims(boolean first, int leadingDims, Tree type) { - if (type == null) { - return null; - } if (first) { return DimensionHelpers.extractDims(type, SortedDims.YES); } @@ -1132,6 +1130,7 @@ public Void visitIf(IfTree node, Void unused) { // ImportTree#isModule() (JEP 511) exists from JDK 23 on; this module compiles with a JDK 21 // compiler, so it can't be referenced directly. + @Nullable private static final Method IMPORT_TREE_IS_MODULE = maybeGetMethod(ImportTree.class, "isModule"); @Override @@ -1467,7 +1466,7 @@ public Void visitMethod(MethodTree node, Void unused) { openedNameAndTypeScope = true; } scan(baseReturnType, null); - maybeAddDims(dims); + maybeAddDims(checkNotNull(dims)); } if (!first) { builder.breakOp(FillMode.INDEPENDENT, " ", ZERO, Optional.of(breakBeforeName)); @@ -1774,7 +1773,7 @@ private boolean ambiguousUnaryOperator(UnaryTree node, String operatorName) { return true; } - private JCTree.Tag unaryTag(ExpressionTree expression) { + private JCTree.@Nullable Tag unaryTag(ExpressionTree expression) { if (expression instanceof UnaryTree) { return ((JCTree) expression).getTag(); } @@ -3110,6 +3109,7 @@ private ExpressionTree getArrayBase(ExpressionTree node) { return node; } + @Nullable private ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { ExpressionTree select = methodInvocation.getMethodSelect(); return select instanceof MemberSelectTree memberSelectTree ? memberSelectTree.getExpression() : null; @@ -3432,7 +3432,7 @@ static int rowLength(List row) { private Integer actualColumn(ExpressionTree expression) { Map positionToColumnMap = builder.getInput().getPositionToColumnMap(); - return positionToColumnMap.get(builder.actualStartColumn(getStartPosition(expression))); + return checkNotNull(positionToColumnMap.get(builder.actualStartColumn(getStartPosition(expression)))); } /** How many lines does this node take up in the input. Returns at least 1. */ @@ -3448,8 +3448,8 @@ int lineSpan(Tree node) { while (endPosition > startPosition && positionTokenMap.get(endPosition) == null) { endPosition--; } - Input.Token startToken = positionTokenMap.get(startPosition); - Input.Token endToken = positionTokenMap.get(endPosition); + Input.Token startToken = checkNotNull(positionTokenMap.get(startPosition)); + Input.Token endToken = checkNotNull(positionTokenMap.get(endPosition)); return lineNumberAt(endToken) - lineNumberAt(startToken) + 1; } @@ -3493,7 +3493,7 @@ int declareOne( DeclarationKind kind, Direction annotationsDirection, Optional modifiers, - Tree type, + @Nullable Tree type, Name name, String op, String equals, @@ -3521,7 +3521,7 @@ int declareOne( DeclarationKind kind, Direction annotationsDirection, Optional modifiers, - Tree type, + @Nullable Tree type, Name name, String op, String equals, @@ -4005,6 +4005,7 @@ protected void sync(Tree node) { * have it. Used with {@link #invoke} to reach AST accessors that are newer than the compiler this module builds * with, such as {@code ImportTree#isModule()} (JDK 23). */ + @Nullable protected static Method maybeGetMethod(Class c, String name) { try { return c.getMethod(name); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java index 9466bd12b..5ddeefca5 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaOutput.java @@ -14,6 +14,8 @@ package com.palantir.javaformat.java; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.base.CharMatcher; import com.google.common.base.MoreObjects; import com.google.common.collect.DiscreteDomain; @@ -277,14 +279,14 @@ public ImmutableList getFormatReplacements(RangeSet iRange replaceFrom--; } - int i = kToJ.get(startTok.getIndex()).lowerEndpoint(); + int i = checkNotNull(kToJ.get(startTok.getIndex())).lowerEndpoint(); // Include leading blank lines from the formatted output, unless the formatted range // starts at the beginning of the file. while (i > 0 && getLine(i - 1).isEmpty()) { i--; } // Write out the formatted range. - for (; i < kToJ.get(endTok.getIndex()).upperEndpoint(); i++) { + for (; i < checkNotNull(kToJ.get(endTok.getIndex())).upperEndpoint(); i++) { // It's possible to run out of output lines (e.g. if the input ended with // multiple trailing newlines). if (i < getLineCount()) { @@ -364,8 +366,8 @@ private Range expandToBreakableRegions(Range iRange) { if (!partialFormatRanges().contains(loTok) || !partialFormatRanges().contains(hiTok)) { return EMPTY_RANGE; } - loTok = partialFormatRanges().rangeContaining(loTok).lowerEndpoint(); - hiTok = partialFormatRanges().rangeContaining(hiTok).upperEndpoint(); + loTok = checkNotNull(partialFormatRanges().rangeContaining(loTok)).lowerEndpoint(); + hiTok = checkNotNull(partialFormatRanges().rangeContaining(hiTok)).upperEndpoint(); return Range.closedOpen(loTok, hiTok + 1); } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java index 9cc61d20a..77baf2c3b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavacTokens.java @@ -28,6 +28,7 @@ import com.sun.tools.javac.parser.UnicodeReader; import com.sun.tools.javac.util.Context; import java.util.Set; +import org.jspecify.annotations.Nullable; /** A wrapper around javac's lexer. */ class JavacTokens { @@ -38,12 +39,16 @@ class JavacTokens { /** An unprocessed input token, including whitespace and comments. */ static class RawTok { + @Nullable private final String stringVal; + + @Nullable private final TokenKind kind; + private final int pos; private final int endPos; - RawTok(String stringVal, TokenKind kind, int pos, int endPos) { + RawTok(@Nullable String stringVal, @Nullable TokenKind kind, int pos, int endPos) { this.stringVal = stringVal; this.kind = kind; this.pos = pos; @@ -51,6 +56,7 @@ static class RawTok { } /** The token kind, or {@code null} for whitespace and comments. */ + @Nullable public TokenKind kind() { return kind; } @@ -66,6 +72,7 @@ public int endPos() { } /** The escaped string value of a literal, or {@code null} for other tokens. */ + @Nullable public String stringVal() { return stringVal; } @@ -154,6 +161,7 @@ static class CommentWithTextAndPosition implements Comment { private final AccessibleReader reader; private final CommentStyle style; + @Nullable private String text = null; public CommentWithTextAndPosition(int pos, int endPos, AccessibleReader reader, CommentStyle style) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java index edb40b3a0..a07871487 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JsonSink.java @@ -15,6 +15,8 @@ */ package com.palantir.javaformat.java; +import static com.google.common.base.Preconditions.checkNotNull; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -31,12 +33,14 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalInt; +import org.jspecify.annotations.Nullable; public final class JsonSink implements Sink { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new Jdk8Module()); private final Map childrenMap = new HashMap<>(); + @Nullable private ObjectNode rootNode; @Override @@ -48,7 +52,7 @@ public FinishExplorationNode startExplorationNode( Optional incomingState) { ObjectNode json; if (parentLevelId.isPresent()) { - json = childrenMap.get(parentLevelId.getAsInt()).addObject(); + json = checkNotNull(childrenMap.get(parentLevelId.getAsInt())).addObject(); } else { json = rootNode = OBJECT_MAPPER.createObjectNode(); } @@ -70,7 +74,7 @@ public FinishExplorationNode startExplorationNode( @Override public FinishLevelNode writeLevelNode(int levelNodeId, int parentExplorationId, State incomingState, Level level) { - ObjectNode json = childrenMap.get(parentExplorationId).addObject(); + ObjectNode json = checkNotNull(childrenMap.get(parentExplorationId)).addObject(); json.put("type", "level"); json.put("id", levelNodeId); json.put("levelId", level.id()); diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java index c63963112..6366f1200 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/ModifierOrderer.java @@ -16,6 +16,7 @@ package com.palantir.javaformat.java; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getLast; @@ -71,6 +72,7 @@ boolean isEmpty() { return tokens.isEmpty() || modifier == null; } + @Nullable Modifier modifier() { return modifier; } @@ -106,7 +108,7 @@ ImmutableList getToksAfter() { @Override public int compareTo(ModifierTokens o) { checkState(!isEmpty()); // empty ModifierTokens are filtered out prior to sorting - return modifier.compareTo(o.modifier); + return checkNotNull(modifier).compareTo(checkNotNull(o.modifier)); } } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java index c27745e4f..242f6be86 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; /** * Removes unused imports from a source file. Imports that are only used in javadoc are also removed, and the references @@ -76,7 +77,7 @@ private static final class UnusedImportScanner extends TreePathScanner usedNames = new LinkedHashSet<>(); - private final Multimap> usedInJavadoc = HashMultimap.create(); + private final Multimap> usedInJavadoc = HashMultimap.create(); final JavacTrees trees; final DocTreeScanner docTreeSymbolScanner; @@ -115,8 +116,10 @@ public Void visitCase(CaseTree tree, Void unused) { return super.visitCase(tree, null); } + @Nullable private static final Method CASE_TREE_GET_LABELS = caseTreeGetLabels(); + @Nullable private static Method caseTreeGetLabels() { try { return CaseTree.class.getMethod("getLabels"); @@ -126,7 +129,7 @@ private static Method caseTreeGetLabels() { } @Override - public Void scan(Tree tree, Void unused) { + public Void scan(@Nullable Tree tree, Void unused) { if (tree == null) { return null; } @@ -219,7 +222,7 @@ private static RangeMap buildReplacements( String contents, JCCompilationUnit unit, Set usedNames, - Multimap> usedInJavadoc) { + Multimap> usedInJavadoc) { RangeMap replacements = TreeRangeMap.create(); String sep = Newlines.guessLineSeparator(contents); // From JDK 23 on, getImports() also returns JCModuleImport, which is not a JCImport, so iterate @@ -293,6 +296,7 @@ private static boolean isBlankLineAfter(String contents, int pos, String sep) { // ImportTree#isModule() (JEP 511) exists from JDK 23 on; this module compiles with a JDK 21 // compiler, so it can't be referenced directly. Same idiom as CASE_TREE_GET_LABELS above. + @Nullable private static final Method IMPORT_TREE_IS_MODULE = JavaInputAstVisitor.maybeGetMethod(ImportTree.class, "isModule"); @@ -312,7 +316,7 @@ private static String getSimpleName(ImportTree importTree) { private static boolean isUnused( JCCompilationUnit unit, Set usedNames, - Multimap> usedInJavadoc, + Multimap> usedInJavadoc, ImportTree importTree, String simpleName) { if (isModuleImport(importTree)) { @@ -354,7 +358,7 @@ private static boolean isOnDemand(ImportTree importTree) { } private static boolean isUsed( - Set usedNames, Multimap> usedInJavadoc, String simpleName) { + Set usedNames, Multimap> usedInJavadoc, String simpleName) { return usedNames.contains(simpleName) || usedInJavadoc.containsKey(simpleName); } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java index 2eba020ef..f90b1b8d5 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Trees.java @@ -142,6 +142,7 @@ static Name getMethodName(MethodInvocationTree methodInvocation) { } /** Returns the receiver of a qualified method invocation expression, or {@code null}. */ + @Nullable static ExpressionTree getMethodReceiver(MethodInvocationTree methodInvocation) { ExpressionTree select = methodInvocation.getMethodSelect(); return select instanceof MemberSelectTree memberSelectTree ? memberSelectTree.getExpression() : null; diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java index 178fd0987..0fac3c4a7 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java @@ -47,19 +47,31 @@ import java.util.List; import java.util.Optional; import javax.lang.model.element.Name; +import org.jspecify.annotations.Nullable; /** * Extends {@link JavaInputAstVisitor} with support for AST nodes that were added or modified for Java 14. */ public class Java14InputAstVisitor extends JavaInputAstVisitor { + @Nullable private static final Method COMPILATION_UNIT_TREE_GET_MODULE = maybeGetMethod(CompilationUnitTree.class, "getModule"); + + @Nullable private static final Method CLASS_TREE_GET_PERMITS_CLAUSE = maybeGetMethod(ClassTree.class, "getPermitsClause"); + + @Nullable private static final Method BINDING_PATTERN_TREE_GET_VARIABLE = maybeGetMethod(BindingPatternTree.class, "getVariable"); + + @Nullable private static final Method BINDING_PATTERN_TREE_GET_TYPE = maybeGetMethod(BindingPatternTree.class, "getType"); + + @Nullable private static final Method BINDING_PATTERN_TREE_GET_BINDING = maybeGetMethod(BindingPatternTree.class, "getBinding"); + + @Nullable private static final Method CASE_TREE_GET_LABELS = maybeGetMethod(CaseTree.class, "getLabels"); public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) { @@ -119,7 +131,7 @@ private static boolean isVarType(Tree type) { return type.getKind().name().equals("VAR_TYPE"); } - private void visitBindingPattern(ModifiersTree modifiers, Tree type, Name name) { + private void visitBindingPattern(@Nullable ModifiersTree modifiers, @Nullable Tree type, Name name) { builder.open(plusFour); if (modifiers != null) { builder.addAll(visitModifiers(modifiers, Direction.HORIZONTAL, Optional.empty())); @@ -344,6 +356,7 @@ public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) { return null; } + @Nullable protected ExpressionTree getGuard(final CaseTree node) { return null; } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java index 703b3ac10..0d80cafa7 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/java21/Java21InputAstVisitor.java @@ -27,6 +27,7 @@ import com.sun.source.tree.PatternTree; import com.sun.source.tree.Tree; import javax.lang.model.element.Name; +import org.jspecify.annotations.Nullable; /** * Extends {@link Java14InputAstVisitor} with support for AST nodes that were added or modified in @@ -42,7 +43,7 @@ public Java21InputAstVisitor(OpsBuilder builder, int indentMultiplier) { } @Override - public Void scan(Tree tree, Void unused) { + public Void scan(@Nullable Tree tree, Void unused) { if (tree != null && tree.getKind().name().equals(ANY_PATTERN_KIND_NAME)) { // No sync(tree): javac records the start position one past the `_`, which makes sync() // think a token was skipped and throw. This branch emits one leaf token and never recurses. diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java index f0fa8c663..9fe734672 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java @@ -543,7 +543,7 @@ private static void deindentPreCodeBlock(ImmutableList.Builder output, Pe if (last.length() > 1) { saved.addLast(new Token( LITERAL, last.getValue().substring(0, last.getValue().length() - 1))); - saved.addLast(new Token(FORCED_NEWLINE, null)); + saved.addLast(new Token(FORCED_NEWLINE, "\n")); } trailingBrace = true; } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java index efa7b6288..6190d5174 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterIntegrationTest.java @@ -14,6 +14,7 @@ package com.palantir.javaformat.java; +import static com.google.common.base.Preconditions.checkNotNull; import static com.palantir.javaformat.java.FileBasedTests.assumeJavaVersionForTest; import static com.palantir.javaformat.java.FileBasedTests.isRecreate; import static org.assertj.core.api.Assertions.assertThat; @@ -43,13 +44,16 @@ public static List data() throws IOException { private final String name; private final String input; private final String expected; - private final String separator; public FormatterIntegrationTest(String name, String input, String expected) { this.name = name; this.input = input; this.expected = expected; - this.separator = isRecreate() ? null : Newlines.getLineEnding(expected); + } + + /** The line separator of the expected output, which the idempotency tests replace with each of the others. */ + private String separator() { + return checkNotNull(Newlines.getLineEnding(expected)); } /** @@ -98,7 +102,7 @@ public void idempotentLF() { assumeJavaVersionForTest(name); Assumptions.assumeFalse(isRecreate(), "Not running when recreating test outputs"); try { - String mangled = expected.replace(separator, "\n"); + String mangled = expected.replace(separator(), "\n"); String output = createFormatter().formatSource(mangled); assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { @@ -111,7 +115,7 @@ public void idempotentCR() { assumeJavaVersionForTest(name); Assumptions.assumeFalse(isRecreate(), "Not running when recreating test outputs"); try { - String mangled = expected.replace(separator, "\r"); + String mangled = expected.replace(separator(), "\r"); String output = createFormatter().formatSource(mangled); assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { @@ -124,7 +128,7 @@ public void idempotentCRLF() { assumeJavaVersionForTest(name); Assumptions.assumeFalse(isRecreate(), "Not running when recreating test outputs"); try { - String mangled = expected.replace(separator, "\r\n"); + String mangled = expected.replace(separator(), "\r\n"); String output = createFormatter().formatSource(mangled); assertThat(output).describedAs("bad output for %s", name).isEqualTo(mangled); } catch (FormatterException e) { diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java index aec3257ab..58203eed4 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java @@ -37,6 +37,7 @@ import java.util.Arrays; import java.util.List; import javax.lang.model.element.Modifier; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -173,7 +174,8 @@ void formattingWithoutRemovingUnusedImports() throws IOException, UsageException "com/palantir/javaformat/java/testimports/A.formatting-and-import-sorting"); } - private void importOrdering(String sortArg, String outputResourceName) throws IOException, UsageException { + private void importOrdering(@Nullable String sortArg, String outputResourceName) + throws IOException, UsageException { Path path = testFolder.resolve("Foo.java"); String inputResourceName = "com/palantir/javaformat/java/testimports/A.input"; diff --git a/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java b/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java index fc3540ac7..e158dde2a 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/jupiter/ParameterizedClass.java @@ -175,7 +175,7 @@ private static List invokeUserParametersMethod(ExtensionContext extens return extensionContext .getStore(namespace) - .getOrComputeIfAbsent( + .computeIfAbsent( "invokeUserParametersMethod", unused -> allParameters(userParametersMethod), List.class); } @@ -205,7 +205,7 @@ private static List allParameters(Method method) { private static String findStringFormatTemplate(ExtensionContext extensionContext, Class testClass) { return extensionContext .getStore(namespace) - .getOrComputeIfAbsent( + .computeIfAbsent( "findStringFormatTemplate", unused -> { List methods = AnnotationUtils.findAnnotatedMethods( From 3c57634dd109e79ca02ac65347932732095ff2f5 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:08:16 +0300 Subject: [PATCH 14/23] Make NullAway an error Its findings are fixed, and the next one now fails the build. --- .../main/groovy/open-java-format.error-prone-conventions.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 3388b9ce8..c9cf82b93 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -11,6 +11,7 @@ dependencies { } nullaway { + severity = net.ltgt.gradle.errorprone.CheckSeverity.ERROR annotatedPackages.add('com.palantir') jspecifyMode = true } From 149bab2363f7b986de14e49d4c941a245fa0f694 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:24:40 +0300 Subject: [PATCH 15/23] Import StandardCharsets.UTF_8 statically The 36 uses of StandardCharsets.UTF_8 now read UTF_8, as the rest of the code already wrote it, and the ten files no longer import StandardCharsets. The import in a MainTest string is input to the formatter and stays. --- .../javaformat/gradle/BundledFormatter.java | 8 ++--- .../gradle/BundledFormatterTest.java | 9 ++--- .../gradle/NativeImageResolutionTest.java | 4 +-- .../gradle/testing/GradleTestProject.java | 7 ++-- .../bootstrap/FormatterCommandRunner.java | 7 ++-- .../javaformat/java/DebugRenderer.java | 5 +-- .../javaformat/java/DiagnosticTest.java | 9 +++-- .../javaformat/java/FormatterTest.java | 14 ++++---- .../javaformat/java/ModuleImportTest.java | 4 +-- .../java/PartialFormattingTest.java | 33 +++++++++---------- 10 files changed, 48 insertions(+), 52 deletions(-) diff --git a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/BundledFormatter.java b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/BundledFormatter.java index 6caba53d8..82b66d93a 100644 --- a/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/BundledFormatter.java +++ b/gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/BundledFormatter.java @@ -14,11 +14,12 @@ package com.palantir.javaformat.gradle; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.DigestInputStream; @@ -86,10 +87,7 @@ static List jars(Path gradleUserHome, ClassLoader plugin) { } private static List list(ClassLoader plugin) { - return new String(resource(plugin, LIST), StandardCharsets.UTF_8) - .lines() - .map(Jar::parse) - .toList(); + return new String(resource(plugin, LIST), UTF_8).lines().map(Jar::parse).toList(); } private static boolean isComplete(Path dir, List list) { diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/BundledFormatterTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/BundledFormatterTest.java index baa6165c2..41049d113 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/BundledFormatterTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/BundledFormatterTest.java @@ -14,6 +14,7 @@ package com.palantir.javaformat.gradle; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -23,7 +24,6 @@ import java.io.UncheckedIOException; import java.net.URL; import java.net.URLClassLoader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; @@ -148,8 +148,7 @@ void a_jar_that_does_not_match_the_list_fails_and_leaves_nothing_behind(@TempDir var bundle = Files.createDirectories(plugin.resolve(BUNDLE)); Files.writeString(bundle.resolve("formatter.jar"), "not what the list says"); Files.writeString( - bundle.resolve("jars.txt"), - sha256("what the list says".getBytes(StandardCharsets.UTF_8)) + " formatter.jar\n"); + bundle.resolve("jars.txt"), sha256("what the list says".getBytes(UTF_8)) + " formatter.jar\n"); try (var loader = new URLClassLoader(new URL[] {plugin.toUri().toURL()}, null)) { assertThatThrownBy(() -> BundledFormatter.jars(gradleUserHome, loader)) @@ -161,9 +160,7 @@ void a_jar_that_does_not_match_the_list_fails_and_leaves_nothing_behind(@TempDir } private static List bundledList() { - return new String(resource(BUNDLE + "jars.txt"), StandardCharsets.UTF_8) - .lines() - .toList(); + return new String(resource(BUNDLE + "jars.txt"), UTF_8).lines().toList(); } private static List bundledNames() { diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageResolutionTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageResolutionTest.java index 5d6f43434..7c0f05b29 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageResolutionTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/NativeImageResolutionTest.java @@ -14,6 +14,7 @@ package com.palantir.javaformat.gradle; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -22,7 +23,6 @@ import com.palantir.javaformat.java.Replacement; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermissions; @@ -154,7 +154,7 @@ private static String lddVersion() { var process = new ProcessBuilder("ldd", "--version") .redirectErrorStream(true) .start(); - var output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + var output = new String(process.getInputStream().readAllBytes(), UTF_8); process.waitFor(); return output.lines().findFirst().orElse(""); } catch (IOException e) { diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/testing/GradleTestProject.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/testing/GradleTestProject.java index d141ce5f3..32706115b 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/testing/GradleTestProject.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/testing/GradleTestProject.java @@ -16,11 +16,12 @@ package com.palantir.javaformat.gradle.testing; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -164,7 +165,7 @@ public GradleTestProject writeFile(String relativePath, String content) { Path target = file(relativePath); try { Files.createDirectories(target.getParent()); - Files.writeString(target, content, StandardCharsets.UTF_8); + Files.writeString(target, content, UTF_8); } catch (IOException e) { throw new UncheckedIOException("Could not write " + relativePath, e); } @@ -189,7 +190,7 @@ public GradleTestProject delete(String relativePath) { public String readFile(String relativePath) { try { - return Files.readString(file(relativePath), StandardCharsets.UTF_8); + return Files.readString(file(relativePath), UTF_8); } catch (IOException e) { throw new UncheckedIOException("Could not read " + relativePath, e); } diff --git a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/FormatterCommandRunner.java b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/FormatterCommandRunner.java index e8b63d221..028fa3475 100644 --- a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/FormatterCommandRunner.java +++ b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/FormatterCommandRunner.java @@ -16,10 +16,11 @@ package com.palantir.javaformat.bootstrap; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.List; import java.util.Optional; @@ -37,7 +38,7 @@ static Optional runWithStdin(List command, String input, Optiona .start(); try (OutputStream outputStream = process.getOutputStream()) { - outputStream.write(input.getBytes(StandardCharsets.UTF_8)); + outputStream.write(input.getBytes(UTF_8)); } // Make sure to drain stdout before waiting for the process to exit as this can result in a deadlock otherwise. @@ -72,7 +73,7 @@ private static boolean isSyntaxError(String stderr) { private static String readToString(InputStream input) throws IOException { try (input) { - return new String(input.readAllBytes(), StandardCharsets.UTF_8); + return new String(input.readAllBytes(), UTF_8); } } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/DebugRenderer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/DebugRenderer.java index 01cebd7f3..11656fb8c 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/DebugRenderer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/DebugRenderer.java @@ -16,6 +16,8 @@ package com.palantir.javaformat.java; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -37,7 +39,6 @@ import com.palantir.javaformat.doc.Token; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -77,7 +78,7 @@ static void render( formatterDecisionsJson); try { - Files.write(getOutputFile(), javascript.getBytes(StandardCharsets.UTF_8)); + Files.write(getOutputFile(), javascript.getBytes(UTF_8)); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java index 6d83af8aa..e7c2e10c9 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java @@ -23,7 +23,6 @@ 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; @@ -148,7 +147,7 @@ void parseError2() throws FormatterException, IOException, UsageException { String input = "class Foo { void f() {\n g() } }"; Path path = testFolder.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(); @@ -165,7 +164,7 @@ void parseError2() throws FormatterException, IOException, UsageException { 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); @@ -181,7 +180,7 @@ void lexError2() throws FormatterException, IOException, UsageException { String input = "class Foo { void f() {\n g('foo'); } }"; Path path = testFolder.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(); @@ -197,7 +196,7 @@ void lexError2() throws FormatterException, IOException, UsageException { @Test 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/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java index 58203eed4..3dd72b85d 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java @@ -17,6 +17,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.palantir.javaformat.java.JavaFormatterOptions.Style; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; @@ -30,7 +31,6 @@ 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 java.time.Duration; @@ -73,7 +73,7 @@ void formatStdinStdoutWithDashFlag() throws Exception { 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(); @@ -93,7 +93,7 @@ void formatLengthUpToEOF() throws Exception { String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; Path path = testFolder.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(); @@ -109,7 +109,7 @@ void formatLengthOutOfRange() throws Exception { String input = "class Foo{}\n"; Path path = testFolder.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(); @@ -181,7 +181,7 @@ private void importOrdering(@Nullable String sortArg, String outputResourceName) String inputResourceName = "com/palantir/javaformat/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(); @@ -192,14 +192,14 @@ private void importOrdering(@Nullable String sortArg, String outputResourceName) 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/open-java-format/src/test/java/com/palantir/javaformat/java/ModuleImportTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/ModuleImportTest.java index e2c7cc43f..96ebbede8 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/ModuleImportTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/ModuleImportTest.java @@ -16,12 +16,12 @@ package com.palantir.javaformat.java; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayInputStream; import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -79,7 +79,7 @@ class Example {} Main main = new Main( new PrintWriter(out, true), new PrintWriter(err, true), - new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))); + new ByteArrayInputStream(input.getBytes(UTF_8))); int exitCode = main.format("-", "--fix-imports-only", "--skip-removing-unused-imports"); assertThat(err.toString()).isEmpty(); assertThat(exitCode).isZero(); diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/PartialFormattingTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/PartialFormattingTest.java index 3b7e7e250..72be6cf1c 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/PartialFormattingTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/PartialFormattingTest.java @@ -25,7 +25,6 @@ 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; @@ -344,7 +343,7 @@ public void testLength() throws Exception { ""); Path path = testFolder.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(); @@ -373,7 +372,7 @@ public void testLengthRange() throws Exception { ""); Path path = testFolder.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(); @@ -416,7 +415,7 @@ public void statementAndComments() throws Exception { ""); Path path = testFolder.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(); @@ -459,7 +458,7 @@ public void statementAndComments2() throws Exception { ""); Path path = testFolder.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(); @@ -502,7 +501,7 @@ public void statementAndComments3() throws Exception { ""); Path path = testFolder.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(); @@ -640,7 +639,7 @@ public void noTokensOnLine() throws Exception { "}"); Path path = testFolder.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(); @@ -853,7 +852,7 @@ public void lineWithTrailingComment() throws Exception { ""); Path path = testFolder.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(); @@ -967,7 +966,7 @@ public void outOfRangeStartLine() throws Exception { ""); Path path = testFolder.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(); @@ -991,7 +990,7 @@ public void outOfRangeEndLine() throws Exception { ""); Path path = testFolder.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(); @@ -1013,7 +1012,7 @@ public void testOutOfRangeLines() throws Exception { ""); Path path = testFolder.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(); @@ -1034,7 +1033,7 @@ public void testEmptyFirstLine() throws Exception { ""); Path path = testFolder.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(); @@ -1053,7 +1052,7 @@ public void testEmptyLastLine() throws Exception { ""); Path path = testFolder.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(); @@ -1286,7 +1285,7 @@ public void partialEnum() throws Exception { }; Path path = testFolder.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(); @@ -1315,7 +1314,7 @@ public void partialModifierOrder() throws Exception { }; Path path = testFolder.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(); @@ -1364,7 +1363,7 @@ public void endOfLine() throws Exception { private String formatMain(String input, String... args) throws Exception { Path path = testFolder.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(); @@ -1497,7 +1496,7 @@ public void b21668189() throws Exception { private String runFormatter(String input, String[] args) throws IOException, UsageException { Path path = testFolder.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(); From 2ade9da457d4e4414a4b3802071fef7c07ee3f65 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:24:46 +0300 Subject: [PATCH 16/23] Turn off three of Picnic's suggestions - LexicographicalAnnotationAttributeListing would sort the values in an annotation's arrays: {false, true} for {true, false} in a dozen @ValueSource tests, and a @CsvSource's rows out of the order they are grouped in. - StringJoining would turn String.format("%s:%s", lower, higher) into String.join(":", String.valueOf(lower), String.valueOf(higher)). - IsInstanceLambdaUsage would turn doc -> doc instanceof Level into Level.class::isInstance. --- .../groovy/open-java-format.error-prone-conventions.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index c9cf82b93..3066a190c 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -31,7 +31,10 @@ tasks.withType(JavaCompile).configureEach { error('RefactorSwitch') error('SameNameButDifferent') error('UnusedVariable') + disable('IsInstanceLambdaUsage') + disable('LexicographicalAnnotationAttributeListing') disable('LexicographicalAnnotationListing') + disable('StringJoining') } } From 11f51fe2e97907aafa4ecaf6e4a32005e845eef6 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:25:53 +0300 Subject: [PATCH 17/23] Make RedundantStringConversion an error Six toString() calls on what string concatenation or Path.of converts anyway are gone: on the diagnostics Main prints, on the paths MainTest expects, and on an environment variable, which is a String already. --- .../groovy/open-java-format.error-prone-conventions.gradle | 1 + .../javaformat/bootstrap/FormatterServicesTest.java | 3 +-- .../src/main/java/com/palantir/javaformat/java/Main.java | 4 ++-- .../test/java/com/palantir/javaformat/java/MainTest.java | 7 ++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 3066a190c..274b7614a 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -28,6 +28,7 @@ tasks.withType(JavaCompile).configureEach { error('FormatStringConcatenation') error('JUnitClassModifiers') error('JUnitMethodDeclaration') + error('RedundantStringConversion') error('RefactorSwitch') error('SameNameButDifferent') error('UnusedVariable') diff --git a/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java b/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java index fe7944af3..f4e11c3f4 100644 --- a/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java +++ b/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java @@ -102,8 +102,7 @@ private static Stream getFormatters() { return Stream.of( new BootstrappingFormatterService( javaBinPath(), Runtime.version().feature(), getClasspath()), - new NativeImageFormatterService( - Path.of(System.getenv("NATIVE_IMAGE_CLASSPATH").toString()))); + new NativeImageFormatterService(Path.of(System.getenv("NATIVE_IMAGE_CLASSPATH")))); } private String getTestResourceContent(String resourceName) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java index 903dfc6e3..ca0373eb7 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Main.java @@ -165,7 +165,7 @@ private int formatFiles( } 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: " @@ -228,7 +228,7 @@ private int formatStdin(CommandLineOptions parameters, JavaFormatterOptions opti } } 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/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java index 800f25998..47a9f817c 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java @@ -439,10 +439,7 @@ void dryRunFiles() throws Exception { assertThat(exitCode).isEqualTo(0); assertThat(out.toString()) - .isEqualTo(b.toAbsolutePath().toString() - + System.lineSeparator() - + c.toAbsolutePath().toString() - + System.lineSeparator()); + .isEqualTo(b.toAbsolutePath() + System.lineSeparator() + c.toAbsolutePath() + System.lineSeparator()); assertThat(err.toString()).isEmpty(); } @@ -495,7 +492,7 @@ void exitIfChangedFiles() throws Exception { Process process = formatterMain(path.toAbsolutePath().toString()).start(); process.waitFor(); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); - assertThat(out).isEqualTo(path.toAbsolutePath().toString() + System.lineSeparator()); + assertThat(out).isEqualTo(path.toAbsolutePath() + System.lineSeparator()); assertThat(process.exitValue()).isEqualTo(1); } From 4b60a20297dae6422561f431779512033e5d9ec4 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:25:54 +0300 Subject: [PATCH 18/23] Make CanonicalAnnotationSyntax an error An annotation attribute with one value no longer has braces around it (@State's storages), and an array of them no trailing comma (SpotlessExcludesTest's @ValueSource, which then fits on one line). --- .../groovy/open-java-format.error-prone-conventions.gradle | 1 + .../palantir/javaformat/gradle/SpotlessExcludesTest.java | 6 +----- .../javaformat/intellij/PalantirJavaFormatSettings.java | 4 +--- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 274b7614a..111e1d94d 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -25,6 +25,7 @@ tasks.withType(JavaCompile).configureEach { nullaway { handleTestAssertionLibraries = true } + error('CanonicalAnnotationSyntax') error('FormatStringConcatenation') error('JUnitClassModifiers') error('JUnitMethodDeclaration') diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/SpotlessExcludesTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/SpotlessExcludesTest.java index 35facc407..7554a5e3e 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/SpotlessExcludesTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/SpotlessExcludesTest.java @@ -93,11 +93,7 @@ void format_ignores_excluded_directories(String srcDir) { } @ParameterizedTest - @ValueSource( - strings = { - "foo/generated_foo/src/bar", - "src/main/java/test/generatedNamespace", - }) + @ValueSource(strings = {"foo/generated_foo/src/bar", "src/main/java/test/generatedNamespace"}) void format_checks_non_generated_files(String srcDir) { project.buildGradle(""" sourceSets { diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index ae3ea7bd0..6a3379b00 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -37,9 +37,7 @@ import static java.util.Optional.ofNullable; -@State( - name = "PalantirJavaFormatSettings", - storages = {@Storage("open-java-format.xml")}) +@State(name = "PalantirJavaFormatSettings", storages = @Storage("open-java-format.xml")) public class PalantirJavaFormatSettings implements PersistentStateComponent { private StoredSettings state = new StoredSettings(); From 3fb96d7ff81f0e723b73c90b0878478ef0196938 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:25:55 +0300 Subject: [PATCH 19/23] Make DirectReturn an error Formatter.formatSourceAndFixImports and JavadocLexer.generateTokens assigned their last value to a variable only to return it; they return it directly. --- .../groovy/open-java-format.error-prone-conventions.gradle | 1 + .../src/main/java/com/palantir/javaformat/java/Formatter.java | 3 +-- .../com/palantir/javaformat/java/javadoc/JavadocLexer.java | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 111e1d94d..b3308ad7d 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -26,6 +26,7 @@ tasks.withType(JavaCompile).configureEach { handleTestAssertionLibraries = true } error('CanonicalAnnotationSyntax') + error('DirectReturn') error('FormatStringConcatenation') error('JUnitClassModifiers') error('JUnitMethodDeclaration') diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java b/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java index ed2549241..8e05831d7 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/Formatter.java @@ -309,8 +309,7 @@ public String formatSourceAndFixImports(String input) throws FormatterException input = ImportOrderer.reorderImports(input, options.style()); input = RemoveUnusedImports.removeUnusedImports(input); String formatted = formatSource(input); - formatted = StringWrapper.wrap(options.maxLineLength(), formatted, this); - return formatted; + return StringWrapper.wrap(options.maxLineLength(), formatted, this); } /** diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java index 9fe734672..72a899b06 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/javadoc/JavadocLexer.java @@ -174,8 +174,7 @@ private ImmutableList generateTokens() throws LexException { result = joinAdjacentLiteralsAndAdjacentWhitespace(result); result = inferParagraphTags(result); result = optionalizeSpacesAfterLinks(result); - result = deindentPreCodeBlocks(result); - return result; + return deindentPreCodeBlocks(result); } private Token readToken() throws LexException { From 26e93583e6ed5d696bd4ec5b779f3463cba169a1 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:41:35 +0300 Subject: [PATCH 20/23] Test that the IntelliJ settings keep the implementation classpath The settings' tests wrote and read back the native binary settings but never a classpath, the one list in them. The new test round-trips two jars through IntelliJ's serializer. --- .../intellij/PalantirJavaFormatSettingsTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java index 8596c2e1f..8a7eae315 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettingsTest.java @@ -19,7 +19,9 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.util.xmlb.XmlSerializer; import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.StoredSettings; +import java.net.URI; import java.nio.file.Path; +import java.util.List; import java.util.Optional; import org.intellij.lang.annotations.Language; import org.junit.jupiter.api.Test; @@ -60,6 +62,18 @@ void keepsTheNativeBinarySettings() throws Exception { assertThat(reloaded.getNativeImagePath()).hasValue(Path.of("/opt/open-java-format")); } + @Test + void keepsTheImplementationClassPath() throws Exception { + var state = new StoredSettings(); + state.setImplementationClassPath(List.of("file:/opt/formatter.jar", "file:/opt/guava.jar")); + + var reloaded = new PalantirJavaFormatSettings(); + reloaded.loadState(deserialize(JDOMUtil.write(XmlSerializer.serialize(state)))); + + assertThat(reloaded.getImplementationClassPath()) + .hasValue(List.of(URI.create("file:/opt/formatter.jar"), URI.create("file:/opt/guava.jar"))); + } + @Test void ignoresTheNativeImageTheGradlePluginUsedToWrite() throws Exception { // Gradle plugins up to 21.0.1 wrote this for openjavaformat.native.formatter=true. The native binary is a From 1bf5401dee3cdcd8db73bc88a75954dc403732d7 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 18:44:03 +0300 Subject: [PATCH 21/23] Make CollectorMutability an error The ten Collectors.toList() calls are now Stream.toList(), as CLAUDE.md asks of new code, and StringWrapper's Collectors.toMap is toUnmodifiableMap. Nothing changed any of these collections after they were built, and none holds a null, so only their mutability differs: the lists go to command lines, records and assertions, and the map is only read. The previous commit's test round-trips the IntelliJ settings' classpath, the one list that goes through a serializer. --- .../groovy/open-java-format.error-prone-conventions.gradle | 1 + .../java/com/palantir/javaformat/gradle/FormatDiffTest.java | 5 ++--- .../com/palantir/javaformat/intellij/FormatterProvider.java | 5 ++--- .../javaformat/intellij/PalantirJavaFormatSettings.java | 5 ++--- .../javaformat/bootstrap/BootstrappingFormatterService.java | 2 +- .../javaformat/bootstrap/NativeImageFormatterService.java | 3 +-- .../palantir/javaformat/bootstrap/FormatterServicesTest.java | 3 +-- .../com/palantir/javaformat/java/JavaInputAstVisitor.java | 3 +-- .../java/com/palantir/javaformat/java/StringWrapper.java | 2 +- 9 files changed, 12 insertions(+), 17 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index b3308ad7d..23494679c 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -26,6 +26,7 @@ tasks.withType(JavaCompile).configureEach { handleTestAssertionLibraries = true } error('CanonicalAnnotationSyntax') + error('CollectorMutability') error('DirectReturn') error('FormatStringConcatenation') error('JUnitClassModifiers') diff --git a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java index 406baf76d..21fdd5075 100644 --- a/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java +++ b/gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/FormatDiffTest.java @@ -34,7 +34,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -58,7 +57,7 @@ void parsing_git_diff_output_works() throws IOException { List strings = FormatDiff.parseGitDiffOutput(example1) .map(FormatDiff.SingleFileDiff::toString) - .collect(Collectors.toList()); + .toList(); assertThat(strings) .containsExactly( "SingleFileDiff{path=build.gradle, lineRanges=[[24..25), [29..30)]}", @@ -121,7 +120,7 @@ private static List getClasspath() throws IOException { .omitEmptyStrings() .splitToStream(Files.readString(CLASSPATH_FILE.toPath())) .map(Path::of) - .collect(Collectors.toList()); + .toList(); } private static Path javaBinPath() { diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java index 7e2c47f84..bca69fe16 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java @@ -36,7 +36,6 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Optional.ofNullable; @@ -92,7 +91,7 @@ private static Optional createFormatter(FormatterCacheKey cach } private static List getProvidedImplementationUrls(List implementationClasspath) { - return implementationClasspath.stream().map(Path::of).collect(Collectors.toList()); + return implementationClasspath.stream().map(Path::of).toList(); } private static List getBundledImplementationUrls() { @@ -127,7 +126,7 @@ private static List getImplementationUrls( private static List listDirAsUrlsUnchecked(Path dir) { try (Stream list = Files.list(dir)) { - return list.collect(Collectors.toList()); + return list.toList(); } catch (IOException e) { throw new UncheckedIOException("Couldn't list dir: " + dir, e); } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 6a3379b00..1306237d6 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Optional; import java.util.jar.JarFile; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Optional.ofNullable; @@ -158,13 +157,13 @@ static class StoredSettings { public void setImplementationClassPath(@Nullable List value) { implementationClassPath = Optional.ofNullable(value) - .map(strings -> strings.stream().map(URI::create).collect(Collectors.toList())); + .map(strings -> strings.stream().map(URI::create).toList()); } @Nullable public List getImplementationClassPath() { return implementationClassPath - .map(paths -> paths.stream().map(URI::toString).collect(Collectors.toList())) + .map(paths -> paths.stream().map(URI::toString).toList()) .orElse(null); } diff --git a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java index 5467b6105..acecabc06 100644 --- a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java +++ b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/BootstrappingFormatterService.java @@ -84,7 +84,7 @@ private ImmutableList getFormatReplacementsInternal(String input, C jvmArgsForVersion(jdkMajorVersion), implementationClassPath, /* outputReplacements= */ true, - ranges.stream().map(RangeUtils::toStringRange).collect(Collectors.toList())); + ranges.stream().map(RangeUtils::toStringRange).toList()); Optional output = FormatterCommandRunner.runWithStdin(command.toArgs(), input, Optional.ofNullable(jdkPath.getParent())); diff --git a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/NativeImageFormatterService.java b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/NativeImageFormatterService.java index bfaa5607b..cfe4e102d 100644 --- a/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/NativeImageFormatterService.java +++ b/open-java-format-jdk-bootstrap/src/main/java/com/palantir/javaformat/bootstrap/NativeImageFormatterService.java @@ -31,7 +31,6 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; public class NativeImageFormatterService implements FormatterService { private static final ObjectMapper MAPPER = @@ -49,7 +48,7 @@ public ImmutableList getFormatReplacements(String input, Collection FormatterNativeImageArgs command = new FormatterNativeImageArgs( nativeImagePath, /* outputReplacements= */ true, - ranges.stream().map(RangeUtils::toStringRange).collect(Collectors.toList())); + ranges.stream().map(RangeUtils::toStringRange).toList()); output = FormatterCommandRunner.runWithStdin( command.toArgs(), input, Optional.ofNullable(nativeImagePath.getParent())); diff --git a/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java b/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java index f4e11c3f4..7626cf4ee 100644 --- a/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java +++ b/open-java-format-jdk-bootstrap/src/test/java/com/palantir/javaformat/bootstrap/FormatterServicesTest.java @@ -32,7 +32,6 @@ import java.nio.file.Path; import java.util.List; import java.util.Locale; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -121,7 +120,7 @@ private static List getClasspath() { .omitEmptyStrings() .splitToStream(classpath) .map(Path::of) - .collect(Collectors.toList()); + .toList(); } private static Path javaBinPath() { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index 3d68ccc9b..a3717902f 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -33,7 +33,6 @@ import static com.sun.source.tree.Tree.Kind.ANNOTATION; import static com.sun.source.tree.Tree.Kind.EXTENDS_WILDCARD; import static com.sun.source.tree.Tree.Kind.STRING_LITERAL; -import static java.util.stream.Collectors.toList; import com.google.common.base.Enums; import com.google.common.base.MoreObjects; @@ -1642,7 +1641,7 @@ private static List handleStream(List parts) { Name name = getMethodName(methodInvocation); return Stream.of("stream", "parallelStream", "toBuilder").anyMatch(name::contentEquals); }) - .collect(toList()); + .toList(); } private static Stream indexes(Stream stream, Predicate predicate) { diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index 74fe6d190..bb35974ea 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -428,7 +428,7 @@ private Map computeCustomTextBlocksIndent(List textB } return textBlockToParent.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> parentToIndent.get(e.getValue()))); + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> parentToIndent.get(e.getValue()))); } private String computePrefixIndentation( From 3dc82da89352851761d1c4e58bf06432848f2bba Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 19:18:29 +0300 Subject: [PATCH 22/23] Make StringConcatToTextBlock an error The Java sources that DiagnosticTest and FormatterTest feed the formatter were strings joined by + or by Joiner.on('\n'). They are text blocks now, ending in \ where the source had no final newline, and Joiner is no longer imported. --- ...java-format.error-prone-conventions.gradle | 1 + .../javaformat/java/DiagnosticTest.java | 27 +++-- .../javaformat/java/FormatterTest.java | 108 ++++++++++++------ 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 23494679c..00b02757c 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -34,6 +34,7 @@ tasks.withType(JavaCompile).configureEach { error('RedundantStringConversion') error('RefactorSwitch') error('SameNameButDifferent') + error('StringConcatToTextBlock') error('UnusedVariable') disable('IsInstanceLambdaUsage') disable('LexicographicalAnnotationAttributeListing') diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java index e7c2e10c9..b57ee476f 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/DiagnosticTest.java @@ -17,7 +17,6 @@ import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Joiner; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -53,19 +52,19 @@ void restoreLocale() throws Exception { @Test void parseError() throws Exception { - String input = Joiner.on('\n') - .join( - "public class InvalidSyntax {", - " private static NumPrinter {", - " public static void print(int n) {", - " System.out.printf(\"%d%n\", n);", - " }", - " }", - "", - " public static void main(String[] args) {", - " NumPrinter.print(args.length);", - " }", - "}"); + String input = """ + public class InvalidSyntax { + private static NumPrinter { + public static void print(int n) { + System.out.printf("%d%n", n); + } + } + + public static void main(String[] args) { + NumPrinter.print(args.length); + } + }\ + """; StringWriter stdout = new StringWriter(); StringWriter stderr = new StringWriter(); diff --git a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java index 3dd72b85d..96ddfadca 100644 --- a/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java +++ b/open-java-format/src/test/java/com/palantir/javaformat/java/FormatterTest.java @@ -22,7 +22,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; -import com.google.common.base.Joiner; import com.google.common.collect.Range; import com.google.common.io.CharStreams; import java.io.ByteArrayInputStream; @@ -70,8 +69,18 @@ void formatNonJavaFiles() throws Exception { @Test void formatStdinStdoutWithDashFlag() throws Exception { - String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n"; - String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; + String input = """ + class Foo{ + void f + () { + } + } + """; + String expectedOutput = """ + class Foo { + void f() {} + } + """; InputStream in = new ByteArrayInputStream(input.getBytes(UTF_8)); StringWriter out = new StringWriter(); @@ -89,8 +98,23 @@ void formatStdinStdoutWithDashFlag() throws Exception { @Test void formatLengthUpToEOF() throws Exception { - String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n\n\n\n\n\n"; - String expectedOutput = "class Foo {\n" + " void f() {}\n" + "}\n"; + String input = """ + class Foo{ + void f + () { + } + } + + + + + + """; + String expectedOutput = """ + class Foo { + void f() {} + } + """; Path path = testFolder.resolve("Foo.java"); Files.write(path, input.getBytes(UTF_8)); @@ -128,19 +152,19 @@ void blankInClassBodyNoTrailing() throws FormatterException { assertThat(output).isEqualTo(expect); } - private static final String UNORDERED_IMPORTS = Joiner.on('\n') - .join( - "import com.google.common.base.Preconditions;", - "", - "import static org.junit.Assert.fail;", - "import static com.google.truth.Truth.assertThat;", - "", - "import org.junit.runners.JUnit4;", - "import org.junit.runner.RunWith;", - "", - "import java.util.List;", - "", - "import javax.annotations.Nullable;"); + private static final String UNORDERED_IMPORTS = """ + import com.google.common.base.Preconditions; + + import static org.junit.Assert.fail; + import static com.google.truth.Truth.assertThat; + + import org.junit.runners.JUnit4; + import org.junit.runner.RunWith; + + import java.util.List; + + import javax.annotations.Nullable;\ + """; @Test void importsNotReorderedByDefault() throws FormatterException { @@ -217,17 +241,27 @@ void throwsFormatterException() throws Exception { @Test void blankLinesImportComment() throws FormatterException { - String withBlank = "package p;\n" - + "\n" - + "/** test */\n" - + "\n" - + "import a.A;\n" - + "\n" - + "class T {\n" - + " A a;\n" - + "}\n"; - String withoutBlank = - "package p;\n" + "\n" + "/** test */\n" + "import a.A;\n" + "\n" + "class T {\n" + " A a;\n" + "}\n"; + String withBlank = """ + package p; + + /** test */ + + import a.A; + + class T { + A a; + } + """; + String withoutBlank = """ + package p; + + /** test */ + import a.A; + + class T { + A a; + } + """; // Formatting deletes the blank line between the "javadoc" and the first import. assertThat(FORMATTER.formatSource(withBlank)).isEqualTo(withoutBlank); @@ -254,13 +288,15 @@ void canParse_java9_private_interface_methods() { @Test void producesFormattingChangesOnAlreadyFormattedFiles() throws FormatterException { Formatter formatter = FORMATTER; - String simpleClass = "package com.palantir;\n" - + "\n" - + "public final class Formatted {\n" - + " public static int count(int number) {\n" - + " return number;\n" - + " }\n" - + "}"; + String simpleClass = """ + package com.palantir; + + public final class Formatted { + public static int count(int number) { + return number; + } + }\ + """; // This is kinda overkill but the point of the test is to show that it // is for sure formatted and still producing a replacement. String formattedClass = formatter.formatSourceAndFixImports(simpleClass); From fbb6a7cee144607a3266ecf9ac76131539469c56 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Sun, 8 Nov 2026 19:18:30 +0300 Subject: [PATCH 23/23] Make EffectivelyPrivate an error Twelve members of private nested classes were public or protected, which reaches no further than the file a private class is in. They have no access modifier now: - the constructors of Obs's two nodes, of ReferenceScanner, and of the test's DelegatingFormatter; - the IntelliJ formatting task's constructor and applyReplacements; - SnippetWrapper's three methods and StringWrapper's getReflowReplacements; - TypeNameClassifier's TyParseState.isSingleUnit and next(), with the five overrides of next() in its constants. --- ...open-java-format.error-prone-conventions.gradle | 1 + .../palantir/javaformat/java/SnippetFormatter.java | 6 +++--- .../PalantirJavaFormatFormattingService.java | 5 ++--- .../PalantirJavaFormatFormattingServiceTest.java | 2 +- .../main/java/com/palantir/javaformat/doc/Obs.java | 4 ++-- .../javaformat/java/RemoveUnusedImports.java | 2 +- .../palantir/javaformat/java/StringWrapper.java | 2 +- .../javaformat/java/TypeNameClassifier.java | 14 +++++++------- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle index 00b02757c..6e798f00e 100644 --- a/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle +++ b/buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle @@ -28,6 +28,7 @@ tasks.withType(JavaCompile).configureEach { error('CanonicalAnnotationSyntax') error('CollectorMutability') error('DirectReturn') + error('EffectivelyPrivate') error('FormatStringConcatenation') error('JUnitClassModifiers') error('JUnitMethodDeclaration') diff --git a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java index 51d1e3410..995f53955 100644 --- a/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java +++ b/open-java-format-eclipse-plugin/src/main/java/com/palantir/javaformat/java/SnippetFormatter.java @@ -42,18 +42,18 @@ private final class SnippetWrapper { int offset; final StringBuilder contents = new StringBuilder(); - public SnippetWrapper append(String str) { + SnippetWrapper append(String str) { contents.append(str); return this; } - public SnippetWrapper appendSource(String source) { + SnippetWrapper appendSource(String source) { this.offset = contents.length(); contents.append(source); return this; } - public void closeBraces(int initialIndent) { + void closeBraces(int initialIndent) { for (int i = initialIndent; --i >= 0; ) { contents.append("\n").append(createIndentationString(i)).append("}"); } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java index c5b677d1d..abe5dee8a 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java @@ -108,8 +108,7 @@ private static final class PalantirJavaFormatFormattingTask implements Formattin private final AsyncFormattingRequest request; private final Optional formatterService; - public PalantirJavaFormatFormattingTask( - AsyncFormattingRequest request, Optional formatterService) { + PalantirJavaFormatFormattingTask(AsyncFormattingRequest request, Optional formatterService) { this.request = request; this.formatterService = formatterService; } @@ -174,7 +173,7 @@ public void run() { } } - public static String applyReplacements(String input, Collection replacementsCollection) { + static String applyReplacements(String input, Collection replacementsCollection) { List replacements = new ArrayList<>(replacementsCollection); replacements.sort(comparing((Replacement r) -> r.getReplaceRange().lowerEndpoint()) .reversed()); diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 111e491ea..4c4ebf3d0 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -126,7 +126,7 @@ private PsiFile createPsiFile(String path, String... contents) throws IOExceptio } private static final class DelegatingFormatter extends PalantirJavaFormatFormattingService { - public DelegatingFormatter() { + DelegatingFormatter() { this.setPluginDescriptor( requireNonNull(FormattingService.EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) .getPluginDescriptor() diff --git a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java index ba0b09dc3..1f215473e 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/doc/Obs.java @@ -95,7 +95,7 @@ private static class LevelNodeImpl extends HasUniqueId implements LevelNode { private final FinishLevelNode finisher; private final int startColumn; - public LevelNodeImpl(Level level, State incomingState, int parentExplorationId, Sink sink) { + LevelNodeImpl(Level level, State incomingState, int parentExplorationId, Sink sink) { this.level = level; this.sink = sink; this.finisher = sink.writeLevelNode(id(), parentExplorationId, incomingState, level); @@ -170,7 +170,7 @@ private static class ExplorationNodeImpl extends HasUniqueId implements Explorat private final FinishExplorationNode finishExplorationNode; private final Optional parentLevel; - public ExplorationNodeImpl( + ExplorationNodeImpl( @Nullable LevelNodeImpl parent, String humanDescription, Sink sink, diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java index 242f6be86..a5441eb24 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/RemoveUnusedImports.java @@ -181,7 +181,7 @@ public Void visitReference(ReferenceTree referenceTree, Void unused) { private class ReferenceScanner extends TreeScanner { private final long basePos; - public ReferenceScanner(long basePos) { + ReferenceScanner(long basePos) { this.basePos = basePos; } diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java index bb35974ea..e7cd74b3a 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/StringWrapper.java @@ -173,7 +173,7 @@ private static class Reflower { this.lineMap = unit.getLineMap(); } - protected TreeRangeMap getReflowReplacements() { + TreeRangeMap getReflowReplacements() { // Paths to string literals that extend past the column limit. List longStringLiterals = new ArrayList<>(); // Paths to text blocks to be re-indented. diff --git a/open-java-format/src/main/java/com/palantir/javaformat/java/TypeNameClassifier.java b/open-java-format/src/main/java/com/palantir/javaformat/java/TypeNameClassifier.java index 8780f73ca..4353b2a8b 100644 --- a/open-java-format/src/main/java/com/palantir/javaformat/java/TypeNameClassifier.java +++ b/open-java-format/src/main/java/com/palantir/javaformat/java/TypeNameClassifier.java @@ -29,7 +29,7 @@ private enum TyParseState { /** The start state. */ START(false) { @Override - public TyParseState next(JavaCaseFormat n) { + TyParseState next(JavaCaseFormat n) { return switch (n) { case UPPERCASE -> // if we see an UpperCamel later, assume this was a class @@ -47,7 +47,7 @@ public TyParseState next(JavaCaseFormat n) { /** The current prefix is a type. */ TYPE(true) { @Override - public TyParseState next(JavaCaseFormat n) { + TyParseState next(JavaCaseFormat n) { return switch (n) { case UPPERCASE, LOWER_CAMEL, LOWERCASE -> TyParseState.FIRST_STATIC_MEMBER; case UPPER_CAMEL -> TyParseState.TYPE; @@ -58,7 +58,7 @@ public TyParseState next(JavaCaseFormat n) { /** The current prefix is a type, followed by a single static member access. */ FIRST_STATIC_MEMBER(true) { @Override - public TyParseState next(JavaCaseFormat n) { + TyParseState next(JavaCaseFormat n) { return TyParseState.REJECT; } }, @@ -66,7 +66,7 @@ public TyParseState next(JavaCaseFormat n) { /** Anything not represented by one of the other states. */ REJECT(false) { @Override - public TyParseState next(JavaCaseFormat n) { + TyParseState next(JavaCaseFormat n) { return TyParseState.REJECT; } }, @@ -74,7 +74,7 @@ public TyParseState next(JavaCaseFormat n) { /** An ambiguous type prefix. */ AMBIGUOUS(false) { @Override - public TyParseState next(JavaCaseFormat n) { + TyParseState next(JavaCaseFormat n) { return switch (n) { case UPPERCASE -> AMBIGUOUS; case LOWER_CAMEL, LOWERCASE -> TyParseState.REJECT; @@ -89,12 +89,12 @@ public TyParseState next(JavaCaseFormat n) { this.isSingleUnit = isSingleUnit; } - public boolean isSingleUnit() { + boolean isSingleUnit() { return isSingleUnit; } /** Transition function. */ - public abstract TyParseState next(JavaCaseFormat n); + abstract TyParseState next(JavaCaseFormat n); } /**