Skip to content

Java 25: strip javac's EOI sentinel from /// markdown doc comment text - #8565

Merged
timtebeek merged 3 commits into
mainfrom
tim/java25-markdown-javadoc-eoi
Aug 20, 2026
Merged

timtebeek merged 3 commits into
mainfrom
tim/java25-markdown-javadoc-eoi

Conversation

@timtebeek

@timtebeek timtebeek commented Aug 19, 2026

Copy link
Copy Markdown
Member

A /// markdown doc comment whose content ends in a backslash round-trips with a stray 0x1A appended, so the file fails the print idempotency check:

java.lang.IllegalStateException: <file> is not print idempotent.
	at org.openrewrite.Parser.requirePrintEqualsInput(Parser.java:52)

Root cause

This one is upstream in javac, not in our code — we copy the string it hands us. com.sun.tools.javac.tree.DCTree.DCRawText#getContent() can return the content of a JEP 467 markdown doc comment with javac's LayoutCharacters.EOI (0x1A) sentinel appended; its escape lookahead runs past the end of the comment buffer. Visible with a plain DocTrees probe, no OpenRewrite involved:

///x\   ->  DCRawText.getContent() = [x\<0x1A>]
///x    ->  DCRawText.getContent() = [x]

Confirmed on both JDK 25.0.3 and JDK 26.0.1, so it isn't something we can wait out upstream. I'll report it to OpenJDK separately.

Reproducer

class Test {
    ///x\
    void m() {}
}

prints back as ///x\<0x1A>.

The practical trigger is a doc comment ending in backslashes. Banner comments hit this, because four-or-more leading slashes still start with /// and javac therefore classifies them as markdown doc comments rather than ordinary line comments:

class Test {
	////Section\\\\
	void first() {}
}

The exact javac predicate is erratic — trailing runs of 1, 2, 4, 5, 7 and 8 backslashes leak but 3 and 6 do not; ///a\b leaks while ///a\bc and ///a\*b do not — so this strips defensively rather than trying to model the condition.

Fix

Strip a trailing sentinel in visitText(String), the single point every raw content string passes through. Leaving it in place corrupts the text and also desynchronizes the cursor, since the sentinel isn't present in source.

Scope

Java 25 parser only, since /// doc comments are JDK 23+.

Testing

Three new tests in JavadocTest.MarkdownDocComment; all three fail on main and pass with the fix. rewrite-java-25:compatibilityTest green.

`DCTree.DCRawText#getContent()` can return the content of a `///` markdown
doc comment with javac's `LayoutCharacters.EOI` (0x1A) sentinel appended --
its escape lookahead runs past the end of the comment buffer. It shows up
when the content ends in a backslash, e.g. a banner comment:

    ////Section\\\\

which round-trips with a trailing 0x1A and fails the print idempotency
check. Reproduces on JDK 25 and 26, so this needs handling on our side.

Strip a trailing sentinel in `visitText`, which every raw content string
passes through. Leaving it in place corrupts the text and also
desynchronizes the cursor, since the sentinel is not present in `source`.

Only the Java 25 parser is affected; `///` doc comments are JDK 23+.
@timtebeek

Copy link
Copy Markdown
Member Author

Split out of #8564 — unrelated root cause, so it can land independently.

@timtebeek
timtebeek marked this pull request as ready for review August 19, 2026 20:52
@timtebeek timtebeek added bug Something isn't working java javadoc parser labels Aug 19, 2026
@timtebeek timtebeek moved this from In Progress to Ready to Review in OpenRewrite Aug 19, 2026
Comment on lines +1069 to +1071
// `DCRawText#getContent()` can return the content of a markdown (`///`) doc comment with javac's
// end-of-input sentinel appended, e.g. when the content ends in a backslash. The sentinel is not
// present in `source`, so leaving it in place both corrupts the text and desynchronizes the cursor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't really find a good way to shorten this oddity to two lines or less

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shortened to two lines in 670e4ef — kept that it's javac's doing, which API, the trailing-backslash trigger, and the cursor consequence:

// javac can append its end-of-input sentinel to `DCRawText#getContent()` for `///` doc comments,
// e.g. when the content ends in a backslash. It is absent from `source`, so it also skews the cursor.

Rebased onto your eb860a2 rather than force-pushing over it.

@timtebeek
timtebeek merged commit 6c9d6ac into main Aug 20, 2026
1 check passed
@timtebeek
timtebeek deleted the tim/java25-markdown-javadoc-eoi branch August 20, 2026 06:48
@github-project-automation github-project-automation Bot moved this from Ready to Review to Done in OpenRewrite Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working java javadoc parser

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants