Skip to content

A line comment before the )) closing a lambda body is joined onto the code line and swallows the parentheses (upstream #1792) #62

Description

@abashev

What

palantir/palantir-java-format#1792 (filed 2026-09-24, no answer yet) reports that // comments sitting right before the )) which close a lambda's parenthesized body and the call around it are joined onto the preceding code line, so the )) ends up inside the comment. The reporter sees it on 2.50.0, 2.80.0 and 2.99.0 in both styles, and says google-java-format 1.36.1 keeps the comments on their own lines.

It reproduces here on main (86cdc74, 2.98.0.3-8-g86cdc74) with the reporter's input:

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);
    }
}

Formatter.formatSource returns:

                .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);

The first comment is glued to ("gamma"), the second to the first, the )) that closed the body and the call are inside the comment, and the comment reflow then rewraps the joined line, which is where // i// comes from. The text does not parse. The CLI notices only because its import pass parses the result again: open-java-format Repro.java prints Repro.java:11:29: error: ')' expected (a position in the output, not the input), exits with 2 and writes nothing. A caller of formatSource gets the text as is. Upstream's output differs from ours only in that .filter( is broken before the lambda; the defect is the same.

Where it stops

One comment line is enough. What decides is the lambda's parenthesized body being broken at its || while i -> ( stays on the line of the call; the dotted chain plays no part:

Input shape Result
check(i -> (a || b … // c … )), a plain call joined, does not parse
the same with a body short enough that (a(i) moves to the line after i -> comment on its own line, parses
.filter(i -> a || b … // c … ), no parentheses around the body comment on its own line, parses
check((a || b … // c … )), parentheses but no lambda comment on its own line, parses
check(a || b … // c … ) and return (a || b … // c … ); comment on its own line, parses

None of the 15,747 files of the JDK 21 sources has a // line directly before )). Five have one before a single ) (SunGraphics2D, LSSerializerImpl, WalkerFactory, XMLGregorianCalendarImpl and foreign/snippet-files/Snippets), and all five format today.

Why

Not looked into yet. A // comment ends its line by construction, so the break after the comment is lost when the lambda body's level is written out. google-java-format lays the same input out correctly, which points at the lambda's own layout rather than at the comment handling both share. The reporter names palantir#1152 (a trailing comment wrapped to the next line, indented wrongly) as possibly related; that one stays valid Java.

Done when

  • the input above formats with each comment on its own line and )) after them, as google-java-format does, and a golden covers it together with the plain-call shape;
  • a second run changes nothing;
  • the five JDK files with a comment before a single ) format as they do today, and a corpus run shows no other change: only files that fail today should move.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions