Merge SwitchStatementWithSingleDefaultInspection into SwitchStatementWithTooFewBranches

Fixes IDEA-199499 Improve "Replace 'switch' with 'if'" action and make it a quick-fix for "Switch has too few branches"
Now default is not counted as branch in SwitchStatementWithTooFewBranches
This commit is contained in:
Tagir Valeev
2018-09-26 17:06:07 +07:00
parent 22d6343b1e
commit 1b4e5c1e39
32 changed files with 454 additions and 284 deletions
@@ -902,11 +902,6 @@
groupKey="group.names.verbose.or.redundant.code.constructs" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="WEAK WARNING"
implementationClass="com.intellij.codeInspection.duplicateExpressions.DuplicateExpressionsInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="SwitchStatementWithSingleDefault"
key="inspection.switch.statement.with.single.default.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.verbose.or.redundant.code.constructs" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="WARNING"
implementationClass="com.siyeh.ig.redundancy.SwitchStatementWithSingleDefaultInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="SortedCollectionWithNonComparableKeys"
key="inspection.sorted.collection.with.non.comparable.keys.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.probable.bugs" groupBundle="messages.InspectionsBundle"
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.CommonQuickFixBundle;
import com.intellij.openapi.editor.Editor;
@@ -12,6 +13,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.BreakConverter;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
@@ -20,10 +22,7 @@ import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class ConvertSwitchToIfIntention implements IntentionAction {
private final PsiSwitchStatement mySwitchExpression;
@@ -83,11 +82,24 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
final PsiClass aClass = PsiUtil.resolveClassInType(switchExpressionType);
useEquals = aClass != null && !aClass.isEnum() && !TypeConversionUtil.isPrimitiveWrapper(aClass.getQualifiedName());
}
PsiCodeBlock body = switchStatement.getBody();
if (body == null) {
return;
}
// Should execute getFallThroughTargets and statementMayCompleteNormally before converting breaks
Set<PsiSwitchLabelStatement> fallThroughTargets = getFallThroughTargets(body);
boolean mayCompleteNormally = ControlFlowUtils.statementMayCompleteNormally(switchStatement);
BreakConverter converter = BreakConverter.from(switchStatement);
if (converter == null) return;
converter.process();
final List<SwitchStatementBranch> allBranches = extractBranches(commentTracker, body, fallThroughTargets);
final String declarationString;
final boolean hadSideEffects;
final String expressionText;
final Project project = switchStatement.getProject();
if (RemoveUnusedVariableUtil.checkSideEffects(switchExpression, null, new ArrayList<>())) {
if (allBranches.stream().mapToInt(br -> br.getCaseValues().size()).sum() > 1 &&
RemoveUnusedVariableUtil.checkSideEffects(switchExpression, null, new ArrayList<>())) {
hadSideEffects = true;
final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
@@ -108,28 +120,88 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
? '(' + switchExpression.getText() + ')'
: switchExpression.getText();
}
final PsiCodeBlock body = switchStatement.getBody();
if (body == null) {
return;
final StringBuilder ifStatementBuilder = new StringBuilder();
boolean firstBranch = true;
SwitchStatementBranch defaultBranch = null;
for (SwitchStatementBranch branch : allBranches) {
if (branch.isDefault()) {
defaultBranch = branch;
}
else {
dumpBranch(branch, expressionText, firstBranch, useEquals, ifStatementBuilder, commentTracker);
firstBranch = false;
}
}
Set<PsiSwitchLabelStatement> fallThroughTargets =
StreamEx.of(body.getStatements())
.pairMap((s1, s2) -> s2 instanceof PsiSwitchLabelStatement && ControlFlowUtils.statementMayCompleteNormally(s1)
? (PsiSwitchLabelStatement)s2 : null)
.nonNull().toSet();
BreakConverter converter = BreakConverter.from(switchStatement);
if (converter == null) return;
converter.process();
boolean unwrapDefault = false;
if (defaultBranch != null && defaultBranch.hasStatements()) {
unwrapDefault = defaultBranch.isAlwaysExecuted() || (switchStatement.getParent() instanceof PsiCodeBlock && !mayCompleteNormally);
if (!unwrapDefault) {
ifStatementBuilder.append("else ");
dumpBody(defaultBranch, ifStatementBuilder, commentTracker);
}
}
String ifStatementText = ifStatementBuilder.toString();
if (ifStatementText.isEmpty()) {
if (!unwrapDefault) return;
ifStatementText = ";";
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
if (hadSideEffects) {
final PsiStatement declarationStatement = factory.createStatementFromText(declarationString, switchStatement);
switchStatement.getParent().addBefore(declarationStatement, switchStatement);
}
final PsiStatement ifStatement = factory.createStatementFromText(ifStatementText, switchStatement);
if (unwrapDefault) {
StringBuilder sb = new StringBuilder();
dumpBody(defaultBranch, sb, commentTracker);
PsiBlockStatement defaultBody = (PsiBlockStatement)factory.createStatementFromText(sb.toString(), switchStatement);
PsiCodeBlock parent = ObjectUtils.tryCast(switchStatement.getParent(), PsiCodeBlock.class);
if (parent == null) {
commentTracker.grabComments(switchStatement);
switchStatement = BlockUtils.expandSingleStatementToBlockStatement(switchStatement);
parent = (PsiCodeBlock)(switchStatement.getParent());
body = Objects.requireNonNull(switchStatement.getBody());
}
PsiElement addedIf = parent.addBefore(ifStatement, switchStatement);
if (!BlockUtils.containsConflictingDeclarations(body, parent)) {
BlockUtils.inlineCodeBlock(switchStatement, defaultBody.getCodeBlock());
}
else {
switchStatement.replace(defaultBody);
}
commentTracker.insertCommentsBefore(addedIf);
if (ifStatementText.equals(";")) {
addedIf.delete();
}
else {
JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedIf);
}
}
else {
JavaCodeStyleManager.getInstance(project)
.shortenClassReferences(commentTracker.replaceAndRestoreComments(switchStatement, ifStatement));
}
}
@NotNull
private static List<SwitchStatementBranch> extractBranches(CommentTracker commentTracker,
PsiCodeBlock body,
Set<PsiSwitchLabelStatement> fallThroughTargets) {
final List<SwitchStatementBranch> openBranches = new ArrayList<>();
final Set<PsiLocalVariable> declaredVariables = new HashSet<>();
final List<SwitchStatementBranch> allBranches = new ArrayList<>();
SwitchStatementBranch currentBranch = null;
final PsiElement[] children = body.getChildren();
boolean defaultAlwaysExecuted = true;
for (int i = 1; i < children.length - 1; i++) {
final PsiElement statement = children[i];
if (statement instanceof PsiSwitchLabelStatement) {
final PsiSwitchLabelStatement label = (PsiSwitchLabelStatement)statement;
if (currentBranch == null || !fallThroughTargets.contains(statement)) {
if (currentBranch != null) {
defaultAlwaysExecuted = false;
}
openBranches.clear();
currentBranch = new SwitchStatementBranch();
currentBranch.addPendingVariableDeclarations(declaredVariables);
@@ -143,6 +215,10 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
}
if (label.isDefaultCase()) {
currentBranch.setDefault();
currentBranch.setAlwaysExecuted(defaultAlwaysExecuted);
if (defaultAlwaysExecuted) {
openBranches.retainAll(Collections.singleton(currentBranch));
}
}
else {
final PsiExpression value = label.getCaseValue();
@@ -161,7 +237,7 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
}
}
for (SwitchStatementBranch branch : openBranches) {
branch.addStatement(statement);
branch.addStatement((PsiStatement)statement);
}
}
else {
@@ -176,31 +252,14 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
}
}
}
final StringBuilder ifStatementText = new StringBuilder();
boolean firstBranch = true;
SwitchStatementBranch defaultBranch = null;
for (SwitchStatementBranch branch : allBranches) {
if (branch.isDefault()) {
defaultBranch = branch;
}
else {
dumpBranch(branch, expressionText, firstBranch, useEquals, ifStatementText, commentTracker);
firstBranch = false;
}
}
if (defaultBranch != null) {
dumpDefaultBranch(defaultBranch, firstBranch, ifStatementText, commentTracker);
}
if (ifStatementText.length() == 0) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
if (hadSideEffects) {
final PsiStatement declarationStatement = factory.createStatementFromText(declarationString, switchStatement);
switchStatement.getParent().addBefore(declarationStatement, switchStatement);
}
final PsiStatement ifStatement = factory.createStatementFromText(ifStatementText.toString(), switchStatement);
commentTracker.replaceAndRestoreComments(switchStatement, ifStatement);
return allBranches;
}
private static Set<PsiSwitchLabelStatement> 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) {
@@ -238,16 +297,6 @@ public class ConvertSwitchToIfIntention implements IntentionAction {
dumpBody(branch, out, commentTracker);
}
private static void dumpDefaultBranch(SwitchStatementBranch defaultBranch,
boolean firstBranch,
@NonNls StringBuilder out,
CommentTracker commentTracker) {
if (!firstBranch) {
out.append("else ");
}
dumpBody(defaultBranch, out, commentTracker);
}
private static void dumpCaseValues(String expressionText,
List<String> caseValues,
boolean useEquals,
@@ -17,28 +17,27 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiStatement;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import java.util.*;
class SwitchStatementBranch {
private final Set<PsiLocalVariable> m_pendingVariableDeclarations =
new HashSet<>(5);
private final List<String> m_caseValues =
new ArrayList<>(2);
private final List<PsiElement> m_bodyElements =
new ArrayList<>(5);
private final List<PsiElement> m_pendingWhiteSpace =
new ArrayList<>(2);
private boolean m_default;
private boolean m_hasStatements;
private final Set<PsiLocalVariable> myPendingVariableDeclarations = new HashSet<>(5);
private final List<String> myCaseValues = new ArrayList<>(2);
private final List<PsiElement> myBodyElements = new ArrayList<>(5);
private final List<PsiElement> myPendingWhiteSpace = new ArrayList<>(2);
private boolean myDefault;
private boolean myHasStatements;
private boolean myAlwaysExecuted;
public void addCaseValue(String labelString) {
m_caseValues.add(labelString);
myCaseValues.add(labelString);
}
public void addStatement(PsiElement statement) {
m_hasStatements = true;
public void addStatement(PsiStatement statement) {
myHasStatements = myHasStatements || !ControlFlowUtils.isEmpty(statement, false, true);
addElement(statement);
}
@@ -47,42 +46,50 @@ class SwitchStatementBranch {
}
private void addElement(PsiElement element) {
m_bodyElements.addAll(m_pendingWhiteSpace);
m_pendingWhiteSpace.clear();
m_bodyElements.add(element);
myBodyElements.addAll(myPendingWhiteSpace);
myPendingWhiteSpace.clear();
myBodyElements.add(element);
}
public void addWhiteSpace(PsiElement statement) {
if (!m_bodyElements.isEmpty()) {
m_pendingWhiteSpace.add(statement);
if (!myBodyElements.isEmpty()) {
myPendingWhiteSpace.add(statement);
}
}
public List<String> getCaseValues() {
return Collections.unmodifiableList(m_caseValues);
return Collections.unmodifiableList(myCaseValues);
}
public List<PsiElement> getBodyElements() {
return Collections.unmodifiableList(m_bodyElements);
return Collections.unmodifiableList(myBodyElements);
}
public boolean isDefault() {
return m_default;
return myDefault;
}
public void setDefault() {
m_default = true;
myDefault = true;
}
boolean isAlwaysExecuted() {
return myAlwaysExecuted;
}
void setAlwaysExecuted(boolean alwaysExecuted) {
myAlwaysExecuted = alwaysExecuted;
}
public boolean hasStatements() {
return m_hasStatements;
return myHasStatements;
}
public void addPendingVariableDeclarations(Set<? extends PsiLocalVariable> vars) {
m_pendingVariableDeclarations.addAll(vars);
myPendingVariableDeclarations.addAll(vars);
}
public Set<PsiLocalVariable> getPendingVariableDeclarations() {
return Collections.unmodifiableSet(m_pendingVariableDeclarations);
return Collections.unmodifiableSet(myPendingVariableDeclarations);
}
}
@@ -1,7 +0,0 @@
<html>
<body>
Reports switch statements which have only <code>default</code> branch and can be replaced with default branch content.
<!-- tooltip end -->
<p><small>New in 2018.3</small></p>
</body>
</html>
@@ -0,0 +1,13 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
if (r) return 1;
else if ("a".equals(s)) {
return 1;
} else if ("b".equals(s)) {
return 2;
} else {
return 3;
}
}
}
@@ -0,0 +1,11 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
if ("a".equals(s)) {
return 1;
} else if ("b".equals(s)) {
return 2;
}
return 3;
}
}
@@ -6,10 +6,7 @@ class X {
if (r) {
return;
}
System.out.println("d");
} else {
System.out.println("d");
}
System.out.println("d");
}
}
@@ -3,16 +3,15 @@ class X {
int m(String s, int x) {
if (x > 0) {
SWITCH:
if ("a".equals(s)) {
System.out.println("a");
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i == x) return 0;
if (i == x * 2) break;
{
if ("a".equals(s)) {
System.out.println("a");
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i == x) return 0;
if (i == x * 2) break;
}
}
System.out.println("d");
} else {
System.out.println("d");
}
} else {
@@ -0,0 +1,9 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
//ignore
if ("x".equals(s)) {
System.out.println("foo");
}
}
}
@@ -0,0 +1,11 @@
// "Replace 'switch' with 'if'" "true"
class X {
void m(String s, boolean r) {
if (r) {
if ("a".equals(s)) {
System.out.println("a");
}
System.out.println("d");
}
}
}
@@ -4,11 +4,11 @@ class X {
//comment1
//comment4
//comment6
//comment7
//comment8
if ("case1".equals(value)) {//comment2
//comment3
} else if ("case2".equals(value)) {//comment5
} else {//comment7
}
}
}
@@ -0,0 +1,12 @@
// "Replace 'switch' with 'if'" "true"
abstract class Test {
abstract Object getObject();
void foo() {
if (RuntimeException.class.equals(getObject().getClass())) {
System.out.println("RuntimeException");
} else {
System.out.println("Other");
}
}
}
@@ -0,0 +1,15 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
if (r) return 1;
else
swi<caret>tch (s) {
case "a":
return 1;
case "b":
return 2;
default:
return 3;
}
}
}
@@ -0,0 +1,13 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
swi<caret>tch (s) {
case "a":
return 1;
case "b":
return 2;
default:
return 3;
}
}
}
@@ -0,0 +1,13 @@
// "Replace 'switch' with 'if'" "true"
class X {
int m(String s, boolean r) {
switc<caret>h(s) {
case "x":
System.out.println("foo");
break;
default: {
//ignore
}
}
}
}
@@ -0,0 +1,12 @@
// "Replace 'switch' with 'if'" "true"
class X {
void m(String s, boolean r) {
if (r)
swi<caret>tch (s) {
case "a":
System.out.println("a");
default:
System.out.println("d");
}
}
}
@@ -0,0 +1,15 @@
// "Replace 'switch' with 'if'" "true"
abstract class Test {
abstract Object getObject();
void foo() {
<caret>switch(getObject().getClass()) {
case RuntimeException.class:
System.out.println("RuntimeException");
break;
default:
System.out.println("Other");
break;
}
}
}
@@ -1,7 +1,7 @@
// "Unwrap 'switch' statement" "true"
class X {
String test(char c) {
if(c == 'a') {
if (c == 'a') {
System.out.println("foo");
} else {
}
@@ -1,7 +1,7 @@
// "Unwrap 'switch' statement" "true"
class X {
String test(char c) {
if(c == 'a') {
if (c == 'a') {
System.out.println("foo");
}
System.out.println("oops");
@@ -1,10 +1,10 @@
// "Unwrap 'switch' statement" "true"
class X {
String test(char c) {
if(c == 'a') {
if (c == 'a') {
System.out.println("foo");
return "";
}
}
System.out.println("oops");
return "";
}
@@ -1,8 +1,8 @@
// "Unwrap 'switch' statement" "true"
class X {
String test(char c) {
if(c == 'a') {
if (c == 'a') {
System.out.println("foo");
}
}
}
}
@@ -1,13 +1,14 @@
// "Unwrap 'switch' statement" "true"
class X {
String test(char c) {
for(int i=0; i<10; i++)
if(c == 'a') {
for(int i=0; i<10; i++) {
if (c == 'a') {
System.out.println("foo");
continue;
}
}
System.out.println("bar");
System.out.println("oops");
}
System.out.println("oops");
return "";
}
}
@@ -1014,9 +1014,6 @@ inspection.duplicate.expressions.reuse.variable.fix.name=Reuse variable ''{0}''
inspection.duplicate.expressions.replace.other.occurrences.fix.family.name=Replace with variable other occurrences of expression
inspection.duplicate.expressions.replace.other.occurrences.fix.name=Replace with ''{0}'' other occurrences of ''{1}''
inspection.switch.statement.with.single.default.display.name='switch' statement with 'default' case only
inspection.switch.statement.with.single.default.message=Switch statement has only 'default' case
inspection.sorted.collection.with.non.comparable.keys.display.name=Sorted collection with non-comparable elements
inspection.sorted.collection.with.non.comparable.keys.message=Construction of sorted collection with non-comparable elements
inspection.sorted.collection.with.non.comparable.keys.option.type.parameters=Don't report non-comparable type parameters
@@ -1249,7 +1249,8 @@ simplifiable.conditional.expression.problem.descriptor=<code>#ref</code> can be
switch.statement.density.min.option=Minimum density of branches: %
switch.statement.density.problem.descriptor=<code>#ref</code> has too low of a branch density ({0}%) #loc
switch.statement.with.too.few.branches.min.option=Minimum number of branches:
switch.statement.with.too.few.branches.problem.descriptor=<code>#ref</code> has too few branches ({0}), and should probably be replaced with an ''if'' statement #loc
switch.statement.with.too.few.branches.problem.descriptor=''switch'' statement has too few case labels ({0}), and should probably be replaced with an ''if'' statement #loc
switch.statement.with.single.default.message='switch' statement has only 'default' case
switch.statement.without.default.ignore.option=Ignore if all cases of an enum type are covered
unnecessary.label.remove.quickfix=Remove label
unnecessary.return.problem.descriptor=<code>#ref</code> is unnecessary as the last statement in a 'void' method #loc
@@ -1,74 +0,0 @@
/*
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.controlflow;
import com.intellij.codeInspection.ui.SingleIntegerFieldOptionsPanel;
import com.intellij.psi.PsiSwitchStatement;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.SwitchUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class SwitchStatementWithTooFewBranchesInspection extends BaseInspection {
private static final int DEFAULT_BRANCH_LIMIT = 2;
@SuppressWarnings("PublicField")
public int m_limit = DEFAULT_BRANCH_LIMIT;
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.display.name");
}
@Override
public JComponent createOptionsPanel() {
return new SingleIntegerFieldOptionsPanel(InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.min.option"),
this, "m_limit");
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
final Integer branchCount = (Integer)infos[0];
return InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.problem.descriptor", branchCount);
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new SwitchStatementWithTooFewBranchesVisitor();
}
private class SwitchStatementWithTooFewBranchesVisitor extends BaseInspectionVisitor {
@Override
public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
final int branchCount = SwitchUtils.calculateBranchCount(statement);
if (branchCount == 0) {
return; // do not warn when no switch branches are present at all
}
final int branchCountIncludingDefault = (branchCount < 0) ? -branchCount + 1 : branchCount;
if (branchCountIncludingDefault >= m_limit) {
return;
}
registerStatementError(statement, Integer.valueOf(branchCountIncludingDefault));
}
}
}
@@ -340,7 +340,16 @@ public final class CommentTracker {
grabComments(element);
}
private void grabComments(PsiElement element) {
/**
* Grab the comments from given element which should be restored. Normally you don't need to call this method.
* It should be called only if element is about to be deleted by other code which is not CommentTracker-aware.
*
* <p>Calling this method repeatedly has no effect. It's also safe to call this method, then delete element using
* other methods from this class like {@link #delete(PsiElement)}.
*
* @param element element to grab the comments from.
*/
public void grabComments(PsiElement element) {
checkState();
for (PsiComment comment : PsiTreeUtil.collectElementsOfType(element, PsiComment.class)) {
if (!shouldIgnore(comment)) {
@@ -1022,16 +1022,16 @@ public class ControlFlowUtils {
* Returns true if given element is an empty statement
*
* @param element element to check
* @param ignoreComments if true, empty statement containing comments is still considered empty
* @param commentIsContent if true, empty statement containing comments is not considered empty
* @param emptyBlocks if true, empty block (or nested empty block like {@code {{}}}) is considered an empty statement
* @return true if given element is an empty statement
*/
public static boolean isEmpty(PsiElement element, boolean ignoreComments, boolean emptyBlocks) {
if (!ignoreComments && element instanceof PsiComment) {
public static boolean isEmpty(PsiElement element, boolean commentIsContent, boolean emptyBlocks) {
if (!commentIsContent && element instanceof PsiComment) {
return true;
}
else if (element instanceof PsiEmptyStatement) {
return !ignoreComments ||
return !commentIsContent ||
PsiTreeUtil.getChildOfType(element, PsiComment.class) == null &&
!(PsiTreeUtil.skipWhitespacesBackward(element) instanceof PsiComment);
}
@@ -1040,7 +1040,7 @@ public class ControlFlowUtils {
}
else if (element instanceof PsiBlockStatement) {
final PsiBlockStatement block = (PsiBlockStatement)element;
return isEmpty(block.getCodeBlock(), ignoreComments, emptyBlocks);
return isEmpty(block.getCodeBlock(), commentIsContent, emptyBlocks);
}
else if (emptyBlocks && element instanceof PsiCodeBlock) {
final PsiCodeBlock codeBlock = (PsiCodeBlock)element;
@@ -1050,7 +1050,7 @@ public class ControlFlowUtils {
}
for (int i = 1; i < children.length - 1; i++) {
final PsiElement child = children[i];
if (!isEmpty(child, ignoreComments, true)) {
if (!isEmpty(child, commentIsContent, true)) {
return false;
}
}
@@ -746,7 +746,7 @@
level="WARNING" implementationClass="com.siyeh.ig.controlflow.SwitchStatementWithConfusingDeclarationInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="SwitchStatementWithTooFewBranches" bundle="com.siyeh.InspectionGadgetsBundle"
key="switch.statement.with.too.few.branches.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.control.flow.issues" enabledByDefault="false" level="WARNING"
groupKey="group.names.control.flow.issues" enabledByDefault="true" level="WARNING"
implementationClass="com.siyeh.ig.controlflow.SwitchStatementWithTooFewBranchesInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="SwitchStatementWithTooManyBranches" bundle="com.siyeh.InspectionGadgetsBundle"
key="switch.statement.with.too.many.branches.display.name" groupBundle="messages.InspectionsBundle"
@@ -0,0 +1,132 @@
/*
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.controlflow;
import com.intellij.codeInsight.daemon.impl.quickfix.ConvertSwitchToIfIntention;
import com.intellij.codeInspection.CommonQuickFixBundle;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ui.SingleIntegerFieldOptionsPanel;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiSwitchLabelStatement;
import com.intellij.psi.PsiSwitchStatement;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.BreakConverter;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class SwitchStatementWithTooFewBranchesInspection extends BaseInspection {
private static final int DEFAULT_BRANCH_LIMIT = 2;
@SuppressWarnings("PublicField")
public int m_limit = DEFAULT_BRANCH_LIMIT;
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.display.name");
}
@Override
public JComponent createOptionsPanel() {
return new SingleIntegerFieldOptionsPanel(InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.min.option"),
this, "m_limit");
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
final Integer branchCount = (Integer)infos[0];
if (branchCount == 0) {
return InspectionGadgetsBundle.message("switch.statement.with.single.default.message");
}
return InspectionGadgetsBundle.message("switch.statement.with.too.few.branches.problem.descriptor", branchCount);
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
final Integer branchCount = (Integer)infos[0];
return (Boolean)infos[1] ? new UnwrapSwitchStatementFix(branchCount) : null;
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new SwitchStatementWithTooFewBranchesVisitor();
}
private class SwitchStatementWithTooFewBranchesVisitor extends BaseInspectionVisitor {
@Override
public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
final PsiCodeBlock body = statement.getBody();
if (body == null) return;
int branches = 0;
boolean defaultFound = false;
for (final PsiSwitchLabelStatement child : PsiTreeUtil.getChildrenOfTypeAsList(body, PsiSwitchLabelStatement.class)) {
if (child.isDefaultCase()) {
defaultFound = true;
}
else if (++branches >= m_limit) {
return;
}
}
if (branches == 0 && !defaultFound) {
// Empty switch is reported by another inspection
return;
}
registerStatementError(statement, Integer.valueOf(branches), BreakConverter.from(statement) != null);
}
}
private static class UnwrapSwitchStatementFix extends InspectionGadgetsFix {
int myBranchCount;
private UnwrapSwitchStatementFix(int branchCount) {
myBranchCount = branchCount;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getName() {
return myBranchCount == 0 ? getFamilyName() : CommonQuickFixBundle.message("fix.replace.x.with.y", PsiKeyword.SWITCH, PsiKeyword.IF);
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return CommonQuickFixBundle.message("fix.unwrap.statement", PsiKeyword.SWITCH);
}
@Override
public void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiSwitchStatement statement = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiSwitchStatement.class);
if (statement == null) return;
ConvertSwitchToIfIntention.doProcessIntention(statement);
}
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2003-2015 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.redundancy;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.BreakConverter;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class SwitchStatementWithSingleDefaultInspection extends AbstractBaseJavaLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitSwitchStatement(PsiSwitchStatement statement) {
PsiCodeBlock body = statement.getBody();
if (body == null) return;
PsiElement anchor = Objects.requireNonNull(statement.getFirstChild());
PsiStatement[] statements = body.getStatements();
if (statements.length == 0) return;
if (!(statements[0] instanceof PsiSwitchLabelStatement) || !((PsiSwitchLabelStatement)statements[0]).isDefaultCase()) return;
if (StreamEx.of(statements).skip(1).anyMatch(PsiSwitchLabelStatement.class::isInstance)) return;
if (BreakConverter.from(statement) == null) return;
holder.registerProblem(anchor, InspectionsBundle.message("inspection.switch.statement.with.single.default.message"),
new UnwrapSwitchStatementFix());
}
};
}
private static class UnwrapSwitchStatementFix implements LocalQuickFix {
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return CommonQuickFixBundle.message("fix.unwrap.statement", PsiKeyword.SWITCH);
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiSwitchStatement statement = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiSwitchStatement.class);
if (statement == null) return;
PsiCodeBlock body = statement.getBody();
if (body == null) return;
BreakConverter breakConverter = BreakConverter.from(statement);
if (breakConverter == null) return;
breakConverter.process();
PsiSwitchLabelStatement defaultCase = PsiTreeUtil.getChildOfType(body, PsiSwitchLabelStatement.class);
if (defaultCase == null || !defaultCase.isDefaultCase()) return;
defaultCase.delete();
PsiElement parent = statement.getParent();
if (!(parent instanceof PsiCodeBlock) || !BlockUtils.containsConflictingDeclarations(body, (PsiCodeBlock)parent)) {
BlockUtils.inlineCodeBlock(statement, body);
}
else {
PsiBlockStatement blockStatement = BlockUtils.createBlockStatement(project);
blockStatement.getCodeBlock().replace(body);
statement.replace(blockStatement);
}
}
}
}
@@ -4,10 +4,32 @@ class SwitchStatementWithTooFewBranches {
void foo(int i) {
switch (i) {}
<warning descr="'switch' has too few branches (1), and should probably be replaced with an 'if' statement">switch</warning> (i) {
<warning descr="'switch' statement has too few case labels (1), and should probably be replaced with an 'if' statement">switch</warning> (i) {
case 1:
System.out.println(i);
}
<warning descr="'switch' statement has too few case labels (1), and should probably be replaced with an 'if' statement">switch</warning>(i) {
case 1:
System.out.println(1);
}
<warning descr="'switch' statement has only 'default' case">switch</warning>(i) {
default:
System.out.println(2);
}
switch(i) {
case 1:
System.out.println(1);
case 2:
System.out.println(2);
}
switch(i) {
case 1:
System.out.println(1);
case 2:
System.out.println(2);
default:
System.out.println(3);
}
switch(i) {
case 1:
break;
@@ -3,14 +3,14 @@ package com.siyeh.ig.fixes;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.siyeh.ig.redundancy.SwitchStatementWithSingleDefaultInspection;
import com.siyeh.ig.controlflow.SwitchStatementWithTooFewBranchesInspection;
import org.jetbrains.annotations.NotNull;
public class UnwrapSwitchStatementFixTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[] {new SwitchStatementWithSingleDefaultInspection()};
return new LocalInspectionTool[] {new SwitchStatementWithTooFewBranchesInspection()};
}
@Override