-
Notifications
You must be signed in to change notification settings - Fork 823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extracting lambda expression - without {} - to a new method creates valid java code #6595
base: master
Are you sure you want to change the base?
Conversation
if (localVariables.contains(e) && usedLocalVariables.get(e) == null) { | ||
|
||
final boolean isUsedInLambda = isLambda | ||
&& treesSeensInSelection.contains(node) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: if PHASE_INSIDE_SELECTION
is active, then the node
is already added in scan()
line 106 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Override | ||
public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { | ||
nesting++; | ||
if (node.equals(firstInSelection)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use ==
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
if (node.equals(firstInSelection)) { | ||
phase = PHASE_INSIDE_SELECTION; | ||
} | ||
isLambda = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm ... what about nested lambdas ? Suppose a code like
Runnable r = () -> new Worker() {
public void n() {
doSomething(variable);
}
public void someOverrideMethod() {
someExecutor.execute(
// selection starts here
() -> whateverMethodCall(variable);
// selection ends here
);
}
}.run();
The lambda
flag becomes true
on the outermost lambda, which is still unrelated to the selection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was checking this use case with nested lambda. It is true, that flag is set for whole lambda. On the other hand it seems to fix the issue. I realized a new occurrence of issue, tested on netbeans 19:
static void dummy(final String first) {
String unused = "unused";
String second = "world";
Runnable r = () -> {
Runnable g = () -> {
final String m = first + second + m1;
System.out.println("m" + m);
};
};
}
select first + second + m1
=> extract => non compilable code:
static void dummy(final String first) {
String unused = "unused";
String second = "world";
Runnable r = () -> {
Runnable g = () -> {
final String m = aaa();
System.out.println("m" + m);
};
};
}
private static String aaa() {
return first + second + m1;
}
with my version it create valid code:
static void dummy(final String first) {
String unused = "unused";
String second = "world";
Runnable r = () -> {
Runnable g = () -> {
final String m = aaa(first, second);
System.out.println("m" + m);
};
};
}
private static String aaa(final String first, String second) {
return first + second + m1;
}
I was testing nested lambada and it works fine, just some a sample:
public static void main(String[] args) {
String unused = "unused";
String first = "hello";
String second = "world";
Function<String,Integer> f1 = t -> {
Function<String,String> f2 = u -> {
final String m = first + second + m1;
System.out.println("m" + m);
final Function<String, Integer> len = s -> (u + s + m + first + m1).length();
System.out.println("len" + len.apply("hello"));
return (u + m + first + m1);
};
return f2.apply(t).length();
};
}
=> various extract to method:
public static void main(String[] args) {
String unused = "unused";
String first = "hello";
String second = "world";
Function<String, Integer> f1 = t -> {
Function<String, String> f2 = u -> {
final String m = z1(first, second);
System.out.println("m" + m);
Function<String, Integer> len = z3(u, m, first);
System.out.println("len" + len.apply("hello"));
return (u + m + first + m1);
};
return z4(f2, t);
};
}
private static int z4(Function<String, String> f2, String t) {
return f2.apply(t).length();
}
private static Function<String, Integer> z3(String u, final String m, String first) {
final Function<String, Integer> len = s -> z2(u, s, m, first);
return len;
}
private static int z2(String u, String s, final String m, String first) {
return (u + s + m + first + m1).length();
}
private static String z1(String first, String second) {
return first + second + m1;
}
What didn't work if I select whole statement with return return f2.apply(t).length();
but this is another issue which I was not able to fix yet (and announced it in the description above)
I admit that isLambda
flag is a bit magic flag, but it works in case of lambda or non-lambda code. At least I was not able to find a use case, which could be broken by this flag.
Do you want me to dig deeper?
…ding functional interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hi @Honza-cz
could you add a test case analog to:
Lines 1092 to 1124 in dc529e6
public void testIntroduceMethod179258() throws Exception { | |
performFixTest("package test;\n" + | |
"public class Test {\n" + | |
" public static void test() {\n" + | |
" String test = null;\n" + | |
" |test = \"foo\";\n" + | |
" if (test == null) {\n" + | |
" System.err.println(1);\n" + | |
" } else {\n" + | |
" System.err.println(2);\n" + | |
" }|\n" + | |
" }\n" + | |
"}", | |
("package test;\n" + | |
"public class Test {\n" + | |
" public static void test() {\n" + | |
" String test = null;\n" + | |
" name();\n" + | |
" }\n" + | |
" private static void name() {\n" + | |
" String test;\n" + | |
" test = \"foo\";\n" + | |
" if (test == null) {\n" + | |
" System.err.println(1);\n" + | |
" } else {\n" + | |
" System.err.println(2);\n" + | |
" }\n" + | |
" }\n" + | |
"}") | |
.replaceAll("[ \t\n]+", " "), | |
new DialogDisplayerImpl3("name", EnumSet | |
.of(Modifier.PRIVATE), true)); | |
} |
|
marks start/end of the selection which is supposed to be extracted.
regarding paperwork:
- please make sure the commit has your full name as author, you can check it by looking at the patch file of the PR: https://github.com/apache/netbeans/pull/6595.patch
- please squash everything to one commit and rebase it on latest master once ready to merge
resolves #6311
Fixes the issue linked to extracting parameterized lambda into a method. I tested manually all cases I have in mind. The fix itself is not nicest, but it works. I was unable to write meaningful automated tests for that class.
source code:
extracting
first + t + second
:extracting
t -> first + t + second
:As I mentioned in bug, lambda with {} still cannot be extracted correctly to a method. The problem is with return type of the newly created method which is taken not from lambda, but from enclosing method. I was not able to resolve this issue yet.