Skip to content

InlineMethod produces non-compilable code and breaks semantics when the target method references itself via ClassName::methodName #9742

Open

Description

@zoukang314

Apache NetBeans version

Apache NetBeans 31

What happened

When I run Refactor → Inline on a static method whose body contains a self method reference of the form ClassName::methodName (i.e. the method takes a reference to itself), NetBeans produces code that:

  1. Does not compile — the inlined body still references the now-deleted method; and
  2. Changes runtime semantics — the method body is incorrectly wrapped into a Runnable lambda that is never invoked, while the last statement is pulled out and executed directly.

Language / Project Type / NetBeans Component

Java SE application

How to reproduce

InlineMethod_S004_T01.java

Before refactoring:

public class InlineMethod_S004_T01 {
    // Target method and self method reference.
    static void ping() {
        Runnable self = InlineMethod_S004_T01::ping;
        System.out.println("ping");
    }
    public static void main(String[] args) {
        ping();
    }
}

Steps to reproduce:

  1. Paste the class above.
  2. Right-click on ping() → Refactor → Inline…
  3. Confirm the dialog.

Actual result (what NetBeans generates):

public class InlineMethod_S004_T01 {
    public static void main(String[] args) {
        Runnable self = () -> {
            Runnable self1 = InlineMethod_S004_T01::ping;
            System.out.println("ping");
        };
        System.out.println("ping");
    }
}

Expected result

Either of the following should happen:

  • Option A (preferred): NetBeans should detect that ping() is self-referenced inside its own body and refuse to inline it, surfacing a warning such as "Cannot inline: the method is referenced inside its own body (method reference InlineMethod_S004_T01::ping). Inlining would remove the method and break the reference."
  • Option B: If inlining is still attempted, the self method reference should be resolved/rewritten to a construct that remains valid after the method is deleted (e.g. a matching local lambda), and the original statements should be placed in the same order as in the source method — not split across an outer statement and an uncalled lambda.

Why the current output is wrong

  1. Compile error. ping() has been deleted, but the lambda body still contains InlineMethod_S004_T01::ping, which now points to a non-existent method. The class fails to compile.
  2. Broken semantics. The original method body consists of two straight-line statements:
Runnable self = InlineMethod_S004_T01::ping;   // statement 1
System.out.println("ping");                     // statement 2

After refactoring, statement 1 is moved into a Runnable that is never .run(), and statement 2 is hoisted to the top level. The local variable self (and the inner self1) are now dead code, and the execution order / lexical context of the original method body is destroyed.
3. Silent no-op lambda. NetBeans introduced a new Runnable self = () -> { ... } that is never invoked — this is not a faithful inline of the original method.

Root cause (analysis)

The InlineMethod refactoring appears to special-case the method reference ClassName::methodName by "lifting" it into a lambda so that the expression type still matches Runnable, but it does so at the statement level rather than the expression level. It wraps the entire original method body in a synthetic lambda assigned to the variable that originally held the method reference, while leaving the trailing System.out.println in place at the call site. This mishandles the case where the method being inlined is itself the target of the method reference.

Did this work correctly in an earlier version?

Apache NetBeans 28

Operating System

Windows 11

JDK

JDK 21

Apache NetBeans packaging

Apache NetBeans binary zip

Anything else

Eclipse and IntelliJ IDEA behave differently on this same sample during differential refactoring testing (we are reproducing cross-IDE refactoring differences as part of an academic study).
The bug is not limited to Runnable — any functional interface target via ClassName::staticMethod inside the method being inlined should trigger the same issue.

Are you willing to submit a pull request?

No

Activity

  1. added
    Java[ci] enable extra Java tests (java.completion, java.source.base, java.hints, refactoring.java, form)
    on Oct 23, 2026
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

    Java[ci] enable extra Java tests (java.completion, java.source.base, java.hints, refactoring.java, form)hintskind:bugBug report or fixneeds:triageRequires attention from one of the committers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions