openrewrite / openrewrite/rewrite

Java parse-to-print idempotence: untyped lambda in super() prints as varprocessBuilder

Open
#8,712 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug java java 25+
Dominant language
Java
Stars
3.7k
Forks
571
Avg merge
13h 12m
Merged PRs (30d)
261

Description

What version of OpenRewrite are you using?

I am using

  • rewrite-java-25 v8.90.3

(JDK 25)

How are you running OpenRewrite?

Custom code using JavaParser.fromJavaVersion().build() to parse a source file (same parser stack recipes use for Java sources).

What is the smallest, simplest way to reproduce the problem?

When a constructor calls super(...) with an untyped lambda argument for a functional-interface parameter, parse-to-print idempotence fails: the parser returns ParseError instead of CompilationUnit. The erroneous tree prints the lambda parameter as varprocessBuilder -> instead of processBuilder ->.

An explicit functional-interface cast makes the source round-trip.

import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaParserResult;

public class ReproduceSuperCallLambdaParseIdempotence {
    public static void main(String[] args) {
        String source = """
            package example.superlambda;

            import java.nio.file.Path;

            interface ProcessLauncher {
                void launch(Object processBuilder);
            }

            class ParentProperties {
                ParentProperties(Object... ignored) {}
            }

            class ParentRunManager {
                ParentRunManager(ParentProperties properties, ProcessLauncher launcher, Object config) {}
            }

            class ChildRunManager extends ParentRunManager {
                ChildRunManager() {
                    super(
                        new ParentProperties(
                            Path.of("build"), Path.of("app.jar"), 1, 1, 0.10, 100, 4096, ""),
                        processBuilder -> {
                            throw new UnsupportedOperationException("never launches processes");
                        },
                        new Object());
                }
            }
            """;

        var ctx = new InMemoryExecutionContext(t -> { throw new RuntimeException(t); });
        JavaParserResult result = JavaParser.fromJavaVersion().build().parse(ctx, source).findFirst().orElseThrow();

        System.out.println("Parse result class: " + result.getClass().getSimpleName());
        if (result instanceof J.ParseError parseError) {
            System.out.println("=== ERRONEOUS PRINT ===");
            System.out.println(parseError.getErroneous().printAll());
        }
    }
}

What did you expect to see?

  • parse(...) yields CompilationUnit (parse-to-print idempotence).
  • Printed source contains processBuilder ->, not varprocessBuilder ->.

What did you see instead?

  • ParseError with parse-to-print idempotence failure.
  • Erroneous print contains varprocessBuilder -> (spurious var fused onto the parameter name).

What is the full stack trace of any errors you encountered?

No exception stack trace — failure is the non-CompilationUnit parse result and non-idempotent print.

Workaround

Add an explicit functional-interface cast on the lambda:

(ProcessLauncher) (processBuilder -> {
    throw new UnsupportedOperationException("never launches processes");
})

After the cast, the same compilation unit parses as CompilationUnit.

Related (not this bug)

  • #5921 — unnamed _ lambda parameters (fixed)
  • #5329 — type annotations on arrays (fixed)

Are you interested in contributing a fix to OpenRewrite?

Yes, happy to contribute a PR or upstream test if you can point to the preferred location in the Java parser/printer.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reproducer through JavaParser.fromJavaVersion().build().parse and inspect the J.ParseError output from printAll(). Trace how an untyped lambda in a super(...) argument is parsed and printed. Done means the source parses as a CompilationUnit and the output contains processBuilder -> rather than varprocessBuilder ->.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.