apache / netbeans

Apache NetBeans
https://netbeans.apache.org/
Apache License 2.0
2.62k stars 841 forks source link

Netbeans "Refactor/Introduce/Extract Method" creates not-compilable / wrong method declaration in case of parametrized lambda expression #6311

Open Honza-cz opened 1 year ago

Honza-cz commented 1 year ago

Apache NetBeans version

Apache NetBeans 18

What happened

When using Optional/Stream and other monads, netbeans is not able to extract method correctly from lambda expression. Input parameters are missing (in some cases).

I am willing to fix it if you can provide a pointer, which module/java class is responsible for that.

How to reproduce

Assume following code:

public class ExtractMethodDemo {

    public static void main(String[] args) {
        String first = "hello";
        String second = "world";

        System.out.println(
                java.util.Optional.ofNullable("-")
                        .map(t -> first + t + second)
                        .orElseThrow()
        );
    }
}

I would like to move code t -> first + t + second to a new method, some kind or parametrized factory, so I select it ALT SHIFT M (extract method)

It creates following not compilable method

public class ExtractMethodDemo {

    public static void main(String[] args) {
        String first = "hello";
        String second = "world";

        System.out.println(java.util.Optional.ofNullable("-")
                        .map(factory())
                        .orElseThrow()
        );
    }

    private static Function<String, String> factory() {
        return t -> first + t + second;
    }
}

Function lacks input parameters as Function<String, String> factory(String first, String second)

Did this work correctly in an earlier version?

No / Don't know

Operating System

Ubuntu

JDK

8/17

Apache NetBeans packaging

Apache NetBeans binary zip

Anything else

No response

Are you willing to submit a pull request?

Yes

Honza-cz commented 1 year ago

I was able to find a class which is responsible for a such refactoring /netbeans/java/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceExpressionBasedMethodFix.java

Honza-cz commented 1 year ago

That code is quite hard to understand. So far, I am bit lost. What I discovered is, that no input params is found. I suspect something is nok in ScanStatement, but not sure what.

Honza-cz commented 1 year ago

So a bit of progress. It seems, all what is needed is to put references to variables into "usedLocalVariable". I made a simple POC, I added the "tempForceUsedLocalVariable" to be able to change the behavior while debugging netbeans:

ScanStatement.java

    @Override
    public Void visitIdentifier(IdentifierTree node, Void p) {
        Element e = info.getTrees().getElement(getCurrentPath());
        if (e != null) {
            boolean tempForceUsedLocalVariable = false;
            if (tempForceUsedLocalVariable){
                usedLocalVariables.put((VariableElement) e, true);
            }

After this modification, input parameters appeared in newly created method. So it seems it points to right direction:

    private static Supplier<String> concatSup(String first, String second) {
        return ()->first+second;
    }

Anyway, I am still a bit confused by the code itself.

Honza-cz commented 11 months ago

I fought with code base to be able to extract lambda expression with {} but I failed.

If I extract return t + text + m1;

from

private int dummy(final String text) {
    Function<String, String> test2 = t -> {
        return t + text + m1;
    };

    return test2.apply(text).length();
}

Netbeans wrongly deduct the return type from method, whic int:

private int dummy(final String text) {
    Function<String, String> test2 = t -> {
        return inner(t, text);
    };

    return test2.apply(text).length();
}

private int inner(String t, final String text) {
    return t + text + m1;
}

I haven't found a way how to make it working so far :(

Honza-cz commented 11 months ago

But at least it works for lambda without {}