redhat-developer / redhat-developer/vscode-java

Inline Method changes behavior for polymorphic method call

Open
#4,425 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
2.3k
Forks
546
Avg merge
20h 1m
Merged PRs (30d)
11

Description

Description

When using Inline Method on a method that is overridden in a subclass, VS Code Java performs the refactoring successfully, but the refactored program changes its runtime behavior.

The original program relies on dynamic dispatch. After inlining the superclass method body, the polymorphic method call is replaced with a fixed value, which breaks the original behavior.

Steps to reproduce

  1. Create the following Java code.
  2. Place the caret on method f in class A.
  3. Invoke Refactor -> Inline Method.
  4. Apply the inline refactoring.
  5. Run the program before and after refactoring.

Original code

package org.example;

public class Main {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();

        a1.test();
        a2.test();
    }
}

class A {

    int f() {
        return 1;
    }

    void test() {
        int x = f(); // Inline target
        System.out.println(x);
    }
}

class B extends A {
    @Override
    int f() {
        return 2;
    }
}

Actual behavior

VS Code Java performs the inline refactoring. The refactored program still compiles, but its runtime behavior changes.

Original output:

1
2

Refactored output:

1
1

Refactored code generated by VS Code Java:

package org.example;

public class Main {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();

        a1.test();
        a2.test();
    }
}

class A {

    void test() {
        int x = 1;
        System.out.println(x);
    }
}

class B extends A {
    @Override
    int f() {
        return 2;
    }
}

The inline refactoring replaces the polymorphic method call f() with the body of A.f(). However, the call inside A.test() is dynamically dispatched at runtime. When test() is invoked on an instance of B, the original program calls B.f() and prints 2.

After refactoring, this dynamic dispatch is removed, so both calls print 1.

Expected behavior

Inline Method should preserve the runtime behavior of the original program.

The refactoring should either:

  1. reject the inline operation, or
  2. report a warning/error indicating that inlining this method may change behavior because the method is overridden and the call is dynamically dispatched.

It should not silently generate behavior-changing code.

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

The issue does not name an implementation file or test. Start by locating the Inline Method refactoring entry point and its existing regression tests, then reproduce the A/B example and trace how overridden methods and dynamically dispatched calls are handled. Done means the operation is rejected or warns about the behavior change, with a regression test covering outputs 1 and 2.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, typescript
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.