From bf9e0ebb5e1747527f094f8593a9e0fa91a5ffa9 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Fri, 25 Sep 2026 11:38:03 +0300 Subject: [PATCH] Do not inline a lambda body whose closing paren carries a comment A "//" comment on its own line before the "))" that close a lambda's parenthesized body and the call around it was joined onto the code line before it, and the "))" ended up inside the comment, so the output no longer parsed (#62, palantir/palantir-java-format#1792). The command line noticed only because its import pass parses the result again and reported a position in the output; a caller of formatSource got the text as it was. An expression lambda's body is laid out by handle_breakOnlyIfInnerLevelsThenFitOnOneLine: when the body does not fit after "->" but its first line does, tryInlinePrefixOntoCurrentLine lays the body's level out "on one line" through tryToLayOutLevelOnOneLine, which marks every break of that level as not taken and only lets the inner levels break. The comments before the closing ")" belong to that same level, with forced breaks around them, and a forced break laid out flat is what put the comment on the code line. The width of the docs before the last inner level was checked, and a forced break there fails that check; the docs after it were not checked at all. The check is now made for the trailing docs too, the way tryBreakInnerLevel already refuses a suffix of infinite width: a body with a comment before its closing token is not inlined and breaks normally, so the body starts on the line after "->" and the comments keep their own lines at the body's indent, as they already did when the body's first line did not fit. A block comment in the same place was written without a space in front of it and moved on a second run; it now gets its own line as well. The golden holds the reporter's input, the same shape as a plain call, and the block comment. The 15,747 files of the JDK 21 sources format exactly as before: none of them has a comment before "))". --- .../com/palantir/javaformat/doc/Level.java | 15 ++++++++- ...2-comment-before-lambda-close-parens.input | 24 ++++++++++++++ ...-comment-before-lambda-close-parens.output | 32 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.input create mode 100644 open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.output 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 4eda2fc2f..4c3b27e0d 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 @@ -339,9 +339,19 @@ private Optional tryInlinePrefixOntoCurrentLine( // Add the width of tokens, breaks before the lastLevel. We must always have space for // these. - List leadingDocs = docs.subList(0, docs.indexOf(lastLevel)); + int lastLevelIndex = docs.indexOf(lastLevel); + List leadingDocs = docs.subList(0, lastLevelIndex); float leadingWidth = getWidth(leadingDocs); + // A forced break after the lastLevel, such as the ones around a // comment that sits before this level's + // closing token, cannot be laid out flat by tryToLayOutLevelOnOneLine: the comment would swallow every token + // after it on the line. Such a level breaks normally instead, as tryBreakInnerLevel refuses it for the same + // reason. (A forced break before the lastLevel makes leadingWidth infinite and fails the check below.) + List trailingDocs = docs.subList(lastLevelIndex + 1, docs.size()); + if (Float.isInfinite(getWidth(trailingDocs))) { + return Optional.empty(); + } + // Potentially add the width of prefixes we want to consider as part of the width that // must fit on the same line, so that we don't accidentally break prefixes when we could // have avoided doing so. @@ -592,6 +602,9 @@ private static Optional tryBreakInnerLevel_checkInner( * Mark breaks in this level as not broken, but lay out the inner levels normally, according to their own * {@link BreakBehaviour}. The resulting {@link State#mustBreak} will be true if this level did not fit on exactly * one line. + * + *

The callers make sure that none of this level's own breaks is forced: a forced break laid out flat would put + * the tokens after a {@code //} comment inside the comment. */ private State tryToLayOutLevelOnOneLine( CommentsHelper commentsHelper, diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.input b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.input new file mode 100644 index 000000000..750e34a5f --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.input @@ -0,0 +1,24 @@ +import java.util.stream.IntStream; + +class Repro { + int find(Item[] items) { + return IntStream.range(0, items.length) + .filter(i -> (items[i].getName().equals("alpha") || items[i].getName().equals("beta") + || items[i].getName().equals("gamma") + // || (items[i].getName().equals("delta") && items.length > i + // && items[i + 1].getName().equals("epsilon")) + )).findFirst().orElse(-1); + } + + void plainCall() { + check(i -> (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(i) || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(i) + // || c(i) + )); + } + + void blockComment() { + check(i -> (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(i) || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(i) + /* block */ + )); + } +} diff --git a/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.output b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.output new file mode 100644 index 000000000..f6b141e66 --- /dev/null +++ b/open-java-format/src/test/resources/com/palantir/javaformat/java/testdata/ojf-issue-62-comment-before-lambda-close-parens.output @@ -0,0 +1,32 @@ +import java.util.stream.IntStream; + +class Repro { + int find(Item[] items) { + return IntStream.range(0, items.length) + .filter(i -> + (items[i].getName().equals("alpha") + || items[i].getName().equals("beta") + || items[i].getName().equals("gamma") + // || (items[i].getName().equals("delta") && items.length > i + // && items[i + 1].getName().equals("epsilon")) + )) + .findFirst() + .orElse(-1); + } + + void plainCall() { + check(i -> + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(i) + || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(i) + // || c(i) + )); + } + + void blockComment() { + check(i -> + (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(i) + || bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(i) + /* block */ + )); + } +}