Skip to content

Keep the rows of a table that follows an operator such as -> or += - #170

Merged
abashev merged 1 commit into
21.X.Xfrom
fix/column-after-operator
Oct 30, 2026
Merged

abashev merged 1 commit into
21.X.Xfrom
fix/column-after-operator

Conversation

@abashev

@abashev abashev commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

A table of arguments or array elements that the author lays out by hand keeps its rows, but it did not when an operator of more than one character came before it on its line. This stayed a table:

return Map.of("one", 1,
              "two", 2);

while this came out as return () -> Map.of("one", 1, "two", 2);, or with one argument per line where that does not fit:

return () -> Map.of("one", 1,
                    "two", 2);

The same went for an array initializer after mode == 0 ?, where one after mode < 1 ? kept its rows.

Why

The lexer splits an operator such as ==, -> or += into toks of one character, since >> cannot be told from two > without the parser. For each of those toks it moved the column on by the whole operator, so every tok after == on its line sat two columns too far right, and after >>>= twelve. argumentsAreTabular is the only reader of these columns: it compares the column each row starts at with the first row's, and a first row shifted that way never matched the rows under it.

The column now moves on by the tok's own text, a one-word change in JavaInput.buildToks. continueTrailingComment from #169 gave this bug as its reason to read the column of a trailing comment in the text. It still reads it there, together with whether code comes before the comment, and its comment now says only that.

Effect

Of the 15,747 files of the JDK 21 sources, 4 format differently, each with a call laid out by hand in pairs after += or ->, which now keeps its pairs as it would after =. In sun/awt/geom/Curve.java:

-                        crossings += pointCrossingsForLine(px, py, curx, cury, movx, movy);
+                        crossings += pointCrossingsForLine(
+                                px, py,
+                                curx, cury,
+                                movx, movy);

The other three are javax/swing/plaf/basic/BasicTableHeaderUI.java (after +=), com/sun/tools/jdeps/Analyzer.java and jdk/internal/classfile/impl/ClassPrinterImpl.java (after ->). The files that change on a second run are the same 67 as before. The output of these tables changes, so the CLI and the plugins format such files differently.

Not in this change

The formatter still only keeps a table it finds in the input; it does not make one. A long Map.of(...) written on one line comes out with one argument per line before and after this change, which is what #31 is about (palantir#1731 for palantir#1602). Two other reasons a hand-made table is not kept stay as well, and both reproduce here: pairs whose values are different kinds of expression (palantir#204), and a block of a single row (google/google-java-format#572).

Tests

The case tablesAfterMultiCharacterOperators, pairs after -> and an array after ==, fails without the fix. FormatterCasesTest 886 → 887. ./gradlew test passes on JDK 21, and the formatter module's 1408 tests on JDK 25 and 27 as well.

The lexer splits an operator such as == or -> into toks of one
character, and it moved the column on by the whole operator for each
of them, so every tok after == on its line sat two columns too far
right, and after >>>= twelve. The detection of tables is the only
reader of those columns: it compares the column each row starts at
with the first row's. A table whose first row came after such an
operator on its line was never recognized, so its pairs of arguments
or its array elements were laid out like any others, on one line or
one per line, while the same table after any other code kept its
rows.

The column now moves on by the text of each tok. The comment in
continueTrailingComment no longer gives this as the reason it reads
the column of a trailing comment in the text, which it still does,
together with whether code comes first.

The case tablesAfterMultiCharacterOperators fails without the fix. Of
the 15,747 files of the JDK 21 sources, 4 format differently, each
with a call laid out by hand in pairs after += or ->, which now keeps
its pairs as it would after =. The files that change on a second run
are the same 67 as before.

Tests on JDK 21: FormatterCasesTest 886 -> 887; 1408 tests in the
module, which pass on JDK 25 and 27 as well.
@abashev
abashev merged commit 6bcacfc into 21.X.X Oct 30, 2026
17 checks passed
@abashev
abashev deleted the fix/column-after-operator branch October 30, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant