Java: Detect which output variables are used after the method call when extracting a method object (IDEA-152688)

This commit is contained in:
Pavel Dolgov
2017-06-14 15:07:32 +03:00
parent 3c9f0dc6b0
commit 7e689ac90c
11 changed files with 211 additions and 9 deletions
@@ -147,6 +147,10 @@ public class ControlFlowWrapper {
return myExitStatements;
}
public List<PsiVariable> filterUsedVariables(PsiVariable[] outputVariables) {
return ControlFlowUtil.filterUsedVariables(myControlFlow, myFlowEnd, outputVariables);
}
public static class ExitStatementsNotSameException extends Exception {}
@@ -874,7 +874,7 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor {
setMethodCall((PsiMethodCallExpression)((PsiLocalVariable)replace.getDeclaredElements()[0]).getInitializer());
}
final List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariables();
final List<PsiVariable> usedVariables = myControlFlowWrapper.filterUsedVariables(myOutputVariables);
Collection<ControlFlowUtil.VariableInfo> reassigned = myControlFlowWrapper.getInitializedTwice();
for (PsiVariable variable : usedVariables) {
String name = variable.getName();
@@ -361,6 +361,19 @@ public class ControlFlowUtil {
return outputVariables;
}
public static List<PsiVariable> filterUsedVariables(ControlFlow flow, int offset, PsiVariable[] variables) {
if (offset >= flow.getSize()) {
return Collections.emptyList();
}
List<PsiVariable> result = new ArrayList<>();
for (PsiVariable variable : variables) {
if (needVariableValueAt(variable, flow, offset)) {
result.add(variable);
}
}
return result;
}
public static Collection<PsiStatement> findExitPointsAndStatements(final ControlFlow flow, final int start, final int end, final IntArrayList exitPoints,
final Class... classesFilter) {
if (end == start) {
@@ -13,15 +13,15 @@ public class ABug {
Inner inner = new Inner(bottles).invoke();
if (inner.is()) return null;
List<String> errors = inner.getErrors();
int money = inner.getMoney();
int nCount = inner.getnCount();
int rCount = inner.getrCount();
int wCount = inner.getwCount();
char[] tripel = inner.getTripel();
boolean no33pr = inner.isNo33pr();
int first = inner.getFirst();
int last = inner.getLast();
char[] tripel = inner.getTripel();
int rCount = inner.getrCount();
int wCount = inner.getwCount();
int nCount = inner.getnCount();
int money = inner.getMoney();
List<String> errors = inner.getErrors();
boolean unhappy = no33pr && first || no33pr && last;
@@ -0,0 +1,29 @@
class Test {
public static class Node {
public int x;
public boolean condition;
public Node next;
}
public static int test(Node cur) {
Node prev = null;
int total = 0;
while (cur != null) {
if (cur.condition) {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
} else {<selection>
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;</selection>
}
}
return total;
}
}
@@ -0,0 +1,61 @@
class Test {
public static class Node {
public int x;
public boolean condition;
public Node next;
}
public static int test(Node cur) {
Node prev = null;
int total = 0;
while (cur != null) {
if (cur.condition) {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
} else {
Inner inner = new Inner(cur, prev, total).invoke();
cur = inner.getCur();
prev = inner.getPrev();
total = inner.getTotal();
}
}
return total;
}
private static class Inner {
private Node cur;
private Node prev;
private int total;
public Inner(Node cur, Node prev, int total) {
this.cur = cur;
this.prev = prev;
this.total = total;
}
public Node getCur() {
return cur;
}
public Node getPrev() {
return prev;
}
public int getTotal() {
return total;
}
public Inner invoke() {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
return this;
}
}
}
@@ -0,0 +1,30 @@
class Test {
public static class Node {
public int x;
public boolean condition;
public Node next;
}
public static int test(Node cur) {
Node prev = null;
int total = 0;
while (cur != null) {
if (cur.condition) {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
} else {<selection>
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;</selection>
return cur != null ? total + cur.x : total;
}
}
return total;
}
}
@@ -0,0 +1,57 @@
class Test {
public static class Node {
public int x;
public boolean condition;
public Node next;
}
public static int test(Node cur) {
Node prev = null;
int total = 0;
while (cur != null) {
if (cur.condition) {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
} else {
Inner inner = new Inner(cur, prev, total).invoke();
cur = inner.getCur();
total = inner.getTotal();
return cur != null ? total + cur.x : total;
}
}
return total;
}
private static class Inner {
private Node cur;
private Node prev;
private int total;
public Inner(Node cur, Node prev, int total) {
this.cur = cur;
this.prev = prev;
this.total = total;
}
public Node getCur() {
return cur;
}
public int getTotal() {
return total;
}
public Inner invoke() {
if (prev != null) {
total += prev.x;
}
prev = cur;
cur = cur.next;
return this;
}
}
}
@@ -3,8 +3,8 @@ class Test {
int i = 0;
Inner inner = new Inner(i).invoke();
int k = inner.getK();
int j = inner.getJ();
int k = inner.getK();
int m = k + j;
}
@@ -4,8 +4,8 @@ class A {
int y = 46;
int inner = 47;
Inner inner1 = new Inner(y).invoke();
y = inner1.getY();
x = inner1.getX();
y = inner1.getY();
x = y + x + 45;
boolean z = true;
if (z) {
@@ -156,6 +156,14 @@ public class ExtractMethodObjectWithMultipleExitPointsTest extends LightRefactor
doTestWithIdeaCodeStyleSettings();
}
public void testOutputVariablesUsedInLoop1() throws Exception {
doTest();
}
public void testOutputVariablesUsedInLoop2() throws Exception {
doTest();
}
private void doTestWithIdeaCodeStyleSettings() throws Exception {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
String oldPrefix = settings.FIELD_NAME_PREFIX;