Repository navigation
Conversation
From JDK 23 on, javac returns a run of `///` lines (JEP 467) as a single comment token,
so every line after the first still carries its source indentation when
`JavaCommentsHelper.wrapLineComments` sees it. The prefix was read from index 0 of that
untrimmed line, came back empty, and the wrapped remainder was emitted with no `///` at
all -- as bare code. The same indentation was then counted twice in the width test, so
lines under the column limit were wrapped as well.
In Palantir style the result compiles, which is the dangerous part:
class P1 {
/// Summary line.
/// This second markdown line is long enough to overflow the palantir one hundred twenty column limit @deprecated
void f() {}
}
formats to the comment, then `@Deprecated` on a line of its own, then the method.
`javap -v` goes from zero Deprecated markers to four, `javac` exits 0, and nothing
downstream notices that the formatter deprecated a method. In Google style the same
input produces output that does not compile (`illegal start of type`), and formatting it
again fails to parse. Both need the `///` block to be indented, which is why a
`column0 == 0` case does not reproduce it.
The prefix and the width budget now come from the trimmed line, which
`indentLineComments` trims and re-indents anyway. An unbreakable token is left long
instead of producing an empty `///` followed by bare text, and the missing-space rule
(`///foo` -> `/// foo`) now applies to every line of the run, so the same source no
longer formats differently depending on whether the running JDK hands the run over as
one token or as one token per line.
JavaCommentsHelperTest builds the multi-line comment token directly, so the JDK 21 test
task covers the bug that only a JDK 23 or later parser can produce; four of its six
tests fail without this change. The end-to-end test in FormatterTest is gated at 23.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Generate changelog in
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
abashev
added a commit
to openjavaformat/open-java-format
that referenced
this pull request
Oct 3, 2026
…cstring-prefix Keep the comment prefix when wrapping a markdown docstring (palantir palantir#1776)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before this PR
From JDK 23 on, javac returns a run of
///markdown doc lines (JEP 467) as a single comment token, so every line after the first still carries its source indentation whenJavaCommentsHelper.wrapLineCommentssees it. Two lines there then go wrong:The wrapped remainder is emitted with no
///at all. In Palantir style the result compiles, which is the dangerous part:formats to
javap -vgoes from 0Deprecatedmarkers to 4,javacexits 0, and nothing downstream notices that the formatter deprecated a method. In Google style the same input produces output that does not compile (illegal start of type), and formatting it again cannot parse it — soformatfollowed byformatDiagnose/spotlessCheckfails on a file the formatter just wrote. Reproduced on JDK 23, 25 and 26, in full format and throughformatSourceAndFixImports, which is what the Gradle plugin's Spotless step reaches viaFormatterService.It needs the
///block to be indented: atcolumn0 == 0the prefix is found correctly, which is why a top-level case does not reproduce it.git log -S lineCommentPrefixdates theprefixvariable to #1672. Before that commit the same input produced// limit @Deprecated— wrong documentation, but valid Java. #1672's test uses a single///line, which is always line 0 of its token and always gets the right prefix, and it runs on the JDK 21 test task where the multi-line path is unreachable. For reference, google-java-format still hardcodes"// "here.After this PR
The prefix and the width budget come from the trimmed line — which
indentLineCommentstrims and re-indents anyway — so:///when it wraps;///followed by bare text;///foo→/// foonow applies to every line of the run, so the same source no longer formats differently depending on whether the running JDK hands the run over as one token or as one token per line.Tests
JavaCommentsHelperTestbuilds the multi-line comment token directly instead of going through javac, so the JDK 21 test task covers a bug only a JDK 23 or later parser can produce. Four of its six tests fail without the one-line change; the other two are regression guards for ordinary//wrapping. The end-to-end case inFormatterTestis gated at 23 with an assumption.Verified by hand with the CLI on JDK 25 and 26: the
@Deprecatedcase stays inside the comment,javap -vreports 0Deprecatedmarkers, the output compiles, and formatting it again is a fixed point../gradlew buildpasses.Not in this PR
The same investigation found more
///issues, all pre-existing and none of them code-corrupting. Listing them so they are not lost; happy to take any of them in follow-ups:{@snippet}, a markdown table or a link-reference definition changes the rendered markdown even when the wrap is syntactically safe — those regions should not be reflowed at all;///Summary.gaining a space raises the run's common indentation, which turns an indented code block into a paragraph (confirmed in the rendered HTML);doc/Comment.java:75-77holds a second, unguarded copy of the missing-space rule, which also breaks a trailing//noinspectionor//$NON-NLS-1$— the exclusion regex inJavaCommentsHelperwas written to prevent exactly that;/// see [List]) is removed as unused, becausegetDocCommentTreereturns nothing for///there.Split out of #1707, whose reviewer asked for these wrapping bugs to be a separate change. This PR does not depend on it and touches no file it touches.
AI Disclosure: This pull request was prepared with the assistance of Claude Code — the investigation, the fix, the tests, and this description — and reviewed by me before submission.