Skip to content

Commit 1791ca8

Browse files
committed
[NETBEANS-8278] Fixing yield handling in introduce method fix.
1 parent 4bc8045 commit 1791ca8

File tree

10 files changed

+467
-30
lines changed

10 files changed

+467
-30
lines changed

java/java.hints/src/org/netbeans/modules/java/hints/errors/Utilities.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
import org.openide.util.Exceptions;
127127
import static com.sun.source.tree.Tree.Kind.*;
128128
import com.sun.source.tree.UnaryTree;
129+
import com.sun.source.tree.YieldTree;
129130
import com.sun.source.util.TreePathScanner;
130131
import com.sun.source.util.Trees;
131132
import com.sun.tools.javac.api.JavacScope;
@@ -1446,6 +1447,11 @@ public Boolean visitReturn(ReturnTree node, Void p) {
14461447
return true;
14471448
}
14481449

1450+
@Override
1451+
public Boolean visitYield(YieldTree node, Void p) {
1452+
return true;
1453+
}
1454+
14491455
@Override
14501456
public Boolean visitBreak(BreakTree node, Void p) {
14511457
Tree target = info.getTreeUtilities().getBreakContinueTarget(getCurrentPath());

java/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceMethodFix.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.sun.source.tree.Tree;
3434
import com.sun.source.tree.TypeParameterTree;
3535
import com.sun.source.tree.VariableTree;
36+
import com.sun.source.tree.YieldTree;
3637
import com.sun.source.util.SourcePositions;
3738
import com.sun.source.util.TreePath;
3839
import java.awt.GraphicsEnvironment;
@@ -262,6 +263,25 @@ static Fix computeIntroduceMethod(CompilationInfo info, int start, int end, Map<
262263
returnType = methodReturnType;
263264
returnAssignTo = null;
264265
declareVariableForReturnValue = false;
266+
} else if (exitsFromAllBranches && scanner.hasYields) {
267+
returnType = info.getTypes().getNullType();
268+
for (TreePath exit : scanner.selectionExits) {
269+
if (exit.getLeaf().getKind() == Tree.Kind.YIELD) {
270+
Tree target = info.getTreeUtilities().getBreakContinueTargetTree(exit);
271+
TreePath switchExpr = exit;
272+
273+
while (switchExpr != null && switchExpr.getLeaf() != target) {
274+
switchExpr = switchExpr.getParentPath();
275+
}
276+
277+
if (switchExpr != null) {
278+
returnType = info.getTrees().getTypeMirror(switchExpr);
279+
break;
280+
}
281+
}
282+
}
283+
returnAssignTo = null;
284+
declareVariableForReturnValue = false;
265285
} else {
266286
returnType = info.getTypes().getNoType(TypeKind.VOID);
267287
returnAssignTo = null;
@@ -628,7 +648,11 @@ private void generateMethodInvocation(List<StatementTree> nueStatements, List<Ex
628648
}
629649
if (branchExit != null) {
630650
if (returnSingleValue) {
631-
nueStatements.add(make.Return(invocation));
651+
if (branchExit.getLeaf().getKind() == Tree.Kind.YIELD) {
652+
nueStatements.add(make.Yield(invocation));
653+
} else {
654+
nueStatements.add(make.Return(invocation));
655+
}
632656
} else {
633657
StatementTree branch = null;
634658
switch (branchExit.getLeaf().getKind()) {
@@ -641,6 +665,9 @@ private void generateMethodInvocation(List<StatementTree> nueStatements, List<Ex
641665
case RETURN:
642666
branch = make.Return(((ReturnTree) branchExit.getLeaf()).getExpression());
643667
break;
668+
case YIELD:
669+
branch = make.Yield(((YieldTree) branchExit.getLeaf()).getValue());
670+
break;
644671
}
645672
if (remappedReturn != null || exitsFromAllBranches) {
646673
nueStatements.add(make.ExpressionStatement(invocation));
@@ -674,6 +701,13 @@ private ReturnTree makeExtractedReturn(boolean forceReturn) {
674701
*/
675702
private void makeReturnsFromExtractedMethod(List<StatementTree> methodStatements) {
676703
if (returnSingleValue) {
704+
for (TreePath resolved : resolvedExits) {
705+
if (resolved.getLeaf().getKind() == Tree.Kind.YIELD) {
706+
YieldTree yield = (YieldTree) resolved.getLeaf();
707+
708+
copy.rewrite(resolved.getLeaf(), make.Return(yield.getValue()));
709+
}
710+
}
677711
return;
678712
}
679713
if (resolvedExits != null) {
@@ -728,7 +762,7 @@ private boolean resolveAndInitialize() {
728762
resolvedExits.add(resolved);
729763
}
730764

731-
returnSingleValue = exitsFromAllBranches && branchExit.getLeaf().getKind() == Tree.Kind.RETURN && outcomeVariable == null && returnType.getKind() != TypeKind.VOID;
765+
returnSingleValue = exitsFromAllBranches && (branchExit.getLeaf().getKind() == Tree.Kind.RETURN || branchExit.getLeaf().getKind() == Tree.Kind.YIELD) && outcomeVariable == null && returnType.getKind() != TypeKind.VOID;
732766
}
733767
// initialization
734768
make = copy.getTreeMaker();

java/java.hints/src/org/netbeans/modules/java/hints/introduce/ScanStatement.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.sun.source.tree.Tree;
3232
import com.sun.source.tree.VariableTree;
3333
import com.sun.source.tree.WhileLoopTree;
34+
import com.sun.source.tree.YieldTree;
3435
import com.sun.source.util.TreePath;
3536
import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
3637
import java.util.ArrayList;
@@ -71,6 +72,7 @@ final class ScanStatement extends ErrorAwareTreePathScanner<Void, Void> {
7172
private final Map<Tree, Iterable<? extends TreePath>> assignmentsForUse;
7273
final Set<TreePathHandle> usedTypeVariables = new HashSet<TreePathHandle>();
7374
boolean hasReturns = false;
75+
boolean hasYields = false;
7476
private boolean hasBreaks = false;
7577
private boolean hasContinues = false;
7678
private boolean secondPass = false;
@@ -236,6 +238,15 @@ public Void visitReturn(ReturnTree node, Void p) {
236238
return super.visitReturn(node, p);
237239
}
238240

241+
@Override
242+
public Void visitYield(YieldTree node, Void p) {
243+
if (isMethodCode() && phase == PHASE_INSIDE_SELECTION) {
244+
selectionExits.add(getCurrentPath());
245+
hasYields = true;
246+
}
247+
return super.visitYield(node, p);
248+
}
249+
239250
@Override
240251
public Void visitBreak(BreakTree node, Void p) {
241252
if (isMethodCode() && phase == PHASE_INSIDE_SELECTION && !treesSeensInSelection.contains(info.getTreeUtilities().getBreakContinueTargetTree(getCurrentPath()))) {
@@ -313,6 +324,7 @@ String verifyExits(boolean exitsFromAllBranches) {
313324
i += hasReturns ? 1 : 0;
314325
i += hasBreaks ? 1 : 0;
315326
i += hasContinues ? 1 : 0;
327+
i += hasYields ? 1 : 0;
316328
if (i > 1) {
317329
return "ERR_Too_Many_Different_Exits"; // NOI18N
318330
}
@@ -325,6 +337,7 @@ String verifyExits(boolean exitsFromAllBranches) {
325337
for (TreePath tp : selectionExits) {
326338
if (tp.getLeaf().getKind() == Tree.Kind.RETURN) {
327339
if (!exitsFromAllBranches) {
340+
//TODO: the same for yield
328341
ReturnTree rt = (ReturnTree) tp.getLeaf();
329342
TreePath currentReturnValue = rt.getExpression() != null ? new TreePath(tp, rt.getExpression()) : null;
330343
if (!returnValueComputed) {

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/UtilitiesTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,27 @@ public void testExitsAllBranchesSwitchDefaultMissing() throws Exception {
448448
false);
449449
}
450450

451+
public void testExitsAllBranchesYield() throws Exception {
452+
performExitsTest(
453+
"""
454+
package test;
455+
public class Test {
456+
public int test(int param) {
457+
return switch (param) {
458+
default -> {
459+
i|f (param == 0) {
460+
yield 0;
461+
} else {
462+
yield 1;
463+
}
464+
}
465+
};
466+
}
467+
}
468+
""",
469+
true);
470+
}
471+
451472
private void performExitsTest(String code, boolean expected) throws Exception {
452473
int caretPos = code.indexOf('|');
453474
code = code.replace("|", "");

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/introduce/IntroduceHintTest.java

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2618,6 +2618,151 @@ public void testIntroduceMethodReturn233433() throws Exception {
26182618
1, 0);
26192619
}
26202620

2621+
public void testYield1() throws Exception {
2622+
sourceLevel = "17";
2623+
performFixTest("""
2624+
package test;
2625+
public class Test {
2626+
private int convert(int a) {
2627+
return switch (a) {
2628+
case 0 -> {
2629+
int i = 0;
2630+
|System.err.println(i);
2631+
int j = 0;
2632+
yield i + j;|
2633+
}
2634+
default -> -1;
2635+
};
2636+
}
2637+
}
2638+
""",
2639+
"""
2640+
package test;
2641+
public class Test {
2642+
private int convert(int a) {
2643+
return switch (a) {
2644+
case 0 -> {
2645+
int i = 0;
2646+
yield name(i);
2647+
}
2648+
default -> -1;
2649+
};
2650+
}
2651+
private int name(int i) {
2652+
System.err.println(i);
2653+
int j = 0;
2654+
return i + j;
2655+
}
2656+
}
2657+
""",
2658+
new DialogDisplayerImpl3("name", null, true),
2659+
1, 0);
2660+
}
2661+
2662+
public void testYield2() throws Exception {
2663+
sourceLevel = "17";
2664+
performFixTest("""
2665+
package test;
2666+
public class Test {
2667+
private Object convert(int a) {
2668+
return switch (a) {
2669+
case 0 -> {
2670+
int i = 0;
2671+
|System.err.println(i);
2672+
if (i == 0) {
2673+
yield "";
2674+
} else {
2675+
yield 1;
2676+
}|
2677+
}
2678+
default -> -1;
2679+
};
2680+
}
2681+
}
2682+
""",
2683+
"""
2684+
package test;
2685+
public class Test {
2686+
private Object convert(int a) {
2687+
return switch (a) {
2688+
case 0 -> {
2689+
int i = 0;
2690+
yield name(i);
2691+
}
2692+
default -> -1;
2693+
};
2694+
}
2695+
private Object name(int i) {
2696+
System.err.println(i);
2697+
if (i == 0) {
2698+
return "";
2699+
} else {
2700+
return 1;
2701+
}
2702+
}
2703+
}
2704+
""",
2705+
new DialogDisplayerImpl3("name", null, true),
2706+
1, 0);
2707+
}
2708+
2709+
public void testYield3() throws Exception {
2710+
sourceLevel = "17";
2711+
performFixTest("""
2712+
package test;
2713+
public class Test {
2714+
private Object convert(int a) {
2715+
return switch (a) {
2716+
case 0 -> {
2717+
int i = 0;
2718+
|System.err.println(i);
2719+
if (i <= 0) {
2720+
if (i == 0) {
2721+
yield "";
2722+
} else {
2723+
System.err.println("wrong");
2724+
yield "";
2725+
}
2726+
}|
2727+
yield 1;
2728+
}
2729+
default -> -1;
2730+
};
2731+
}
2732+
}
2733+
""",
2734+
"""
2735+
package test;
2736+
public class Test {
2737+
private Object convert(int a) {
2738+
return switch (a) {
2739+
case 0 -> {
2740+
int i = 0;
2741+
if (name(i))
2742+
yield "";
2743+
yield 1;
2744+
}
2745+
default -> -1;
2746+
};
2747+
}
2748+
private boolean name(int i) {
2749+
System.err.println(i);
2750+
if (i <= 0) {
2751+
if (i == 0) {
2752+
return true;
2753+
} else {
2754+
System.err.println("wrong");
2755+
return true;
2756+
}
2757+
}
2758+
return false;
2759+
}
2760+
}
2761+
""",
2762+
new DialogDisplayerImpl3("name", null, true),
2763+
1, 0);
2764+
}
2765+
26212766
protected void prepareTest(String code) throws Exception {
26222767
clearWorkDir();
26232768

java/java.source.base/apichanges.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@
2525
<apidef name="javasource_base">Java Source API</apidef>
2626
</apidefs>
2727
<changes>
28+
<change id="TreeMaker.Yield">
29+
<api name="javasource_base" />
30+
<summary>Added TreeMaker.Yield</summary>
31+
<version major="1" minor="2.75"/>
32+
<date day="18" month="3" year="2025"/>
33+
<author login="jlahoda"/>
34+
<compatibility addition="yes" binary="compatible" source="compatible"/>
35+
<description>
36+
Adding TreeMaker.Yield.
37+
</description>
38+
<class name="TreeMaker" package="org.netbeans.api.java.source"/>
39+
</change>
2840
<change id="SourceUtils.classNameFor-nestedClass">
2941
<api name="javasource_base" />
3042
<summary>SourceUtils.classNameFor nested classes support</summary>

java/java.source.base/nbproject/project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ javadoc.name=Java Source Base
2323
javadoc.title=Java Source Base
2424
javadoc.arch=${basedir}/arch.xml
2525
javadoc.apichanges=${basedir}/apichanges.xml
26-
spec.version.base=2.75.0
26+
spec.version.base=2.76.0
2727
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
2828
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
2929
${o.n.core.dir}/lib/boot.jar:\

java/java.source.base/src/org/netbeans/api/java/source/TreeMaker.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,17 @@ public ReturnTree Return(ExpressionTree expression) {
11001100
return delegate.Return(expression);
11011101
}
11021102

1103+
/**
1104+
* Creates a new YieldTree.
1105+
*
1106+
* @param expression the expression to be yielded.
1107+
* @see com.sun.source.tree.YieldTree
1108+
* @since 2.76
1109+
*/
1110+
public YieldTree Yield(ExpressionTree expression) {
1111+
return delegate.Yield(expression);
1112+
}
1113+
11031114
/**
11041115
* Creates a new SwitchTree.
11051116
*

0 commit comments

Comments
 (0)