ConvertSwitchToIfIntention: support Java 12 switch statements (no expressions)

This commit is contained in:
Tagir Valeev
2018-12-07 11:56:15 +07:00
parent 985d882482
commit bb990f4f13
11 changed files with 172 additions and 47 deletions
@@ -24,12 +24,13 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.stream.Collectors;
public class ConvertSwitchToIfIntention implements IntentionAction {
private final PsiSwitchStatement mySwitchExpression;
private final PsiSwitchStatement mySwitchStatement;
public ConvertSwitchToIfIntention(@NotNull PsiSwitchStatement switchStatement) {
mySwitchExpression = switchStatement;
mySwitchStatement = switchStatement;
}
@NotNull
@@ -46,7 +47,7 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return isAvailable(mySwitchExpression);
return isAvailable(mySwitchStatement);
}
public static boolean isAvailable(PsiSwitchStatement switchStatement) {
@@ -55,10 +56,10 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
}
private static boolean mayFallThroughNonTerminalDefaultCase(PsiCodeBlock body) {
List<PsiSwitchLabelStatement> labels = PsiTreeUtil.getChildrenOfTypeAsList(body, PsiSwitchLabelStatement.class);
List<PsiSwitchLabelStatementBase> labels = PsiTreeUtil.getChildrenOfTypeAsList(body, PsiSwitchLabelStatementBase.class);
return StreamEx.of(labels).pairMap((prev, next) -> {
if (prev.isDefaultCase()) {
Set<PsiSwitchLabelStatement> targets = getFallThroughTargets(body);
Set<PsiSwitchLabelStatementBase> targets = getFallThroughTargets(body);
return targets.contains(prev) || targets.contains(next);
}
return false;
@@ -67,13 +68,13 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
doProcessIntention(mySwitchExpression);
doProcessIntention(mySwitchStatement);
}
@NotNull
@Override
public PsiElement getElementToMakeWritable(@NotNull PsiFile file) {
return mySwitchExpression;
return mySwitchStatement;
}
@Override
@@ -103,7 +104,7 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
return;
}
// Should execute getFallThroughTargets and statementMayCompleteNormally before converting breaks
Set<PsiSwitchLabelStatement> fallThroughTargets = getFallThroughTargets(body);
Set<PsiSwitchLabelStatementBase> fallThroughTargets = getFallThroughTargets(body);
boolean mayCompleteNormally = ControlFlowUtils.statementMayCompleteNormally(switchStatement);
BreakConverter converter = BreakConverter.from(switchStatement);
if (converter == null) return;
@@ -205,7 +206,7 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
@NotNull
private static List<SwitchStatementBranch> extractBranches(CommentTracker commentTracker,
PsiCodeBlock body,
Set<PsiSwitchLabelStatement> fallThroughTargets) {
Set<PsiSwitchLabelStatementBase> fallThroughTargets) {
final List<SwitchStatementBranch> openBranches = new ArrayList<>();
final Set<PsiElement> declaredElements = new HashSet<>();
final List<SwitchStatementBranch> allBranches = new ArrayList<>();
@@ -232,17 +233,29 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
openBranches.add(currentBranch);
}
if (label.isDefaultCase()) {
currentBranch.setDefault();
currentBranch.setAlwaysExecuted(defaultAlwaysExecuted);
if (defaultAlwaysExecuted) {
openBranches.retainAll(Collections.singleton(currentBranch));
}
}
else {
final PsiExpression value = label.getCaseValue();
final String valueText = getCaseValueText(value, commentTracker);
currentBranch.addCaseValue(valueText);
currentBranch.addCaseValues(label, commentTracker);
}
else if (statement instanceof PsiSwitchLabeledRuleStatement) {
openBranches.clear();
defaultAlwaysExecuted = false;
PsiSwitchLabeledRuleStatement rule = (PsiSwitchLabeledRuleStatement)statement;
currentBranch = new SwitchStatementBranch();
PsiStatement ruleBody = rule.getBody();
if (ruleBody != null) {
currentBranch.addStatement(ruleBody);
}
if (rule.isDefaultCase()) {
currentBranch.setAlwaysExecuted(defaultAlwaysExecuted);
}
currentBranch.addCaseValues(rule, commentTracker);
openBranches.add(currentBranch);
allBranches.add(currentBranch);
}
else {
if (statement instanceof PsiStatement) {
@@ -269,33 +282,11 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
return allBranches;
}
private static Set<PsiSwitchLabelStatement> getFallThroughTargets(PsiCodeBlock body) {
private static Set<PsiSwitchLabelStatementBase> getFallThroughTargets(PsiCodeBlock body) {
return StreamEx.of(body.getStatements())
.pairMap((s1, s2) -> s2 instanceof PsiSwitchLabelStatement && ControlFlowUtils.statementMayCompleteNormally(s1)
? (PsiSwitchLabelStatement)s2 : null)
.nonNull().toSet();
}
private static String getCaseValueText(PsiExpression value, CommentTracker commentTracker) {
value = PsiUtil.skipParenthesizedExprDown(value);
if (value == null) {
return "";
}
if (!(value instanceof PsiReferenceExpression)) {
return commentTracker.text(value);
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)value;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiEnumConstant)) {
return commentTracker.text(value);
}
final PsiEnumConstant enumConstant = (PsiEnumConstant)target;
final PsiClass aClass = enumConstant.getContainingClass();
if (aClass == null) {
return commentTracker.text(value);
}
return aClass.getQualifiedName() + '.' + commentTracker.text(referenceExpression);
.pairMap((s1, s2) -> s2 instanceof PsiSwitchLabelStatement && !(s1 instanceof PsiSwitchLabeledRuleStatement) &&
ControlFlowUtils.statementMayCompleteNormally(s1) ? (PsiSwitchLabelStatement)s2 : null)
.nonNull().collect(Collectors.toSet());
}
private static void dumpBranch(SwitchStatementBranch branch,
@@ -338,10 +329,10 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
if (!bodyStatements.isEmpty()) {
PsiElement firstBodyElement = bodyStatements.get(0);
PsiElement prev = PsiTreeUtil.skipWhitespacesAndCommentsBackward(firstBodyElement);
if (prev instanceof PsiSwitchLabelStatement) {
PsiExpression value = ((PsiSwitchLabelStatement)prev).getCaseValue();
if (value != null) {
out.append(CommentTracker.commentsBetween(value, firstBodyElement));
if (prev instanceof PsiSwitchLabelStatementBase) {
PsiExpressionList values = ((PsiSwitchLabelStatementBase)prev).getCaseValues();
if (values != null) {
out.append(CommentTracker.commentsBetween(values, firstBodyElement));
}
}
}
@@ -15,8 +15,9 @@
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import java.util.*;
@@ -91,4 +92,41 @@ class SwitchStatementBranch {
public Set<PsiElement> getPendingDeclarations() {
return Collections.unmodifiableSet(myPendingDeclarations);
}
void addCaseValues(PsiSwitchLabelStatementBase label, CommentTracker commentTracker) {
if (label.isDefaultCase()) {
setDefault();
}
else {
PsiExpressionList values = label.getCaseValues();
if (values != null) {
for (PsiExpression value : values.getExpressions()) {
final String valueText = getCaseValueText(value, commentTracker);
addCaseValue(valueText);
}
}
}
}
private static String getCaseValueText(PsiExpression value, CommentTracker commentTracker) {
value = PsiUtil.skipParenthesizedExprDown(value);
if (value == null) {
return "";
}
if (!(value instanceof PsiReferenceExpression)) {
return commentTracker.text(value);
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)value;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiEnumConstant)) {
return commentTracker.text(value);
}
final PsiEnumConstant enumConstant = (PsiEnumConstant)target;
final PsiClass aClass = enumConstant.getContainingClass();
if (aClass == null) {
return commentTracker.text(value);
}
return aClass.getQualifiedName() + '.' + commentTracker.text(referenceExpression);
}
}
@@ -0,0 +1,12 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
if (x == 0 || x == 1) {
System.out.println("ready");
System.out.println("steady");
} else if (x == 2 || x == 3) {
System.out.println("steady");
}
System.out.println("go");
}
}
@@ -0,0 +1,16 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
//1
/*2*/
/*3*/
/*4*/
/*6*/
/*7*/
if (x == 0) {
System.out.println("zero");/*5*/
} else {
System.out.println("non-zero");/*8*/
}
}
}
@@ -0,0 +1,10 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
if (x == 0) {
System.out.println(x);
} else if (x == 1) {
System.out.println("one");
}
}
}
@@ -0,0 +1,13 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
if (x == 0 || x == 1) {
throw new IllegalArgumentException();
} else if (x == 2 || x == 3) {
if (Math.random() > 0.5) return;
System.out.println("two or three");
} else if (x == 4) {
System.out.println("four");
}
}
}
@@ -0,0 +1,10 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
switch<caret> (x) {
case 0,1: System.out.println("ready");
case 2,3: System.out.println("steady");
default: System.out.println("go");
}
}
}
@@ -0,0 +1,10 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
switch<caret> (x) {
//1
case /*2*/0/*3*/ -> /*4*/System.out.println("zero");/*5*/
default /*6*/->/*7*/ System.out.println("non-zero");/*8*/
}
}
}
@@ -0,0 +1,9 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
switch<caret> (x) {
case 0 -> System.out.println(x);
case 1 -> System.out.println("one");
}
}
}
@@ -0,0 +1,16 @@
// "Replace 'switch' with 'if'" "true"
class Test {
void foo(int x) {
switch<caret> (x) {
case 0,1 -> throw new IllegalArgumentException();
case 2,3 -> {
if (Math.random() > 0.5) break;
System.out.println("two or three");
}
case 4 -> System.out.println("four");
default -> {
break;
}
}
}
}
@@ -58,7 +58,7 @@ class SwitchPredicate implements PsiElementPredicate {
}
final PsiStatement[] statements = body.getStatements();
for (PsiStatement statement : statements) {
if (statement instanceof PsiSwitchLabelStatement && !((PsiSwitchLabelStatement)statement).isDefaultCase()) {
if (statement instanceof PsiSwitchLabelStatementBase && !((PsiSwitchLabelStatementBase)statement).isDefaultCase()) {
return true;
}
}