Java: When merging identical branches of 'switch' statement keep the default branch the last (IDEA-204134)

This commit is contained in:
Pavel Dolgov
2018-12-13 16:33:44 +03:00
parent bcfe6c79d1
commit 63ffc6782b
33 changed files with 472 additions and 62 deletions
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Predicate;
import static com.siyeh.ig.migration.TryWithIdenticalCatchesInspection.collectCommentTexts;
import static com.siyeh.ig.migration.TryWithIdenticalCatchesInspection.getCommentText;
@@ -54,28 +55,39 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
int size = branches.size();
if (size > 1) {
boolean[] isDuplicate = new boolean[size];
int defaultIndex = ContainerUtil.indexOf(branches, Branch::isDefault);
if (defaultIndex >= 0) {
Branch defaultBranch = branches.get(defaultIndex);
for (int index = 0; index < size; index++) {
if (index != defaultIndex) {
Branch branch = branches.get(index);
if (areDuplicates(defaultBranch, branch)) {
isDuplicate[index] = isDuplicate[defaultIndex] = true;
highlightDefaultDuplicate(branch.myStatements);
}
}
}
}
for (int index = 0; index < size - 1; index++) {
if (isDuplicate[index]) continue;
Branch branch = branches.get(index);
for (int otherIndex = index + 1; otherIndex < size; otherIndex++) {
Branch branch = branches.get(index);
if (isDuplicate[otherIndex]) continue;
Branch otherBranch = branches.get(otherIndex);
if (areDuplicates(branch, otherBranch)) {
isDuplicate[otherIndex] = true;
registerProblem(otherBranch.myStatements, branch.getSwitchLabelText());
if (!isDuplicate[index]) {
isDuplicate[index] = true;
registerProblem(branch.myStatements, null);
}
highlightDuplicate(otherBranch.myStatements, branch.getSwitchLabelText());
}
}
}
}
}
private void registerProblem(@NotNull PsiStatement[] statements, String switchLabelText) {
private void highlightDuplicate(@NotNull PsiStatement[] statements, String switchLabelText) {
ProblemDescriptor descriptor = InspectionManager.getInstance(myHolder.getProject())
.createProblemDescriptor(statements[0], statements[statements.length - 1],
InspectionsBundle.message("inspection.duplicate.branches.in.switch.message"),
@@ -83,6 +95,15 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
switchLabelText != null ? new MergeBranchesFix(switchLabelText) : null);
myHolder.registerProblem(descriptor);
}
private void highlightDefaultDuplicate(PsiStatement[] statements) {
ProblemDescriptor descriptor = InspectionManager.getInstance(myHolder.getProject())
.createProblemDescriptor(statements[0], statements[statements.length - 1],
InspectionsBundle.message("inspection.duplicate.branches.in.switch.redundant.message"),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myHolder.isOnTheFly(),
new DeleteRedundantBranchFix(), new MergeWithDefaultBranchFix());
myHolder.registerProblem(descriptor);
}
}
@NotNull
@@ -171,7 +192,7 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
@Contract("null -> false")
private static boolean isBreakWithoutLabel(@Nullable PsiStatement statement) {
return statement instanceof PsiBreakStatement && ((PsiBreakStatement)statement).getLabelIdentifier() == null;
return statement instanceof PsiBreakStatement && ((PsiBreakStatement)statement).getLabelExpression() == null;
}
private static class MergeBranchesFix implements LocalQuickFix {
@@ -197,53 +218,120 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement startElement = descriptor.getStartElement();
FixContext context = new FixContext();
if (context.prepare(descriptor.getStartElement(), branch -> mySwitchLabelText.equals(branch.getSwitchLabelText()))) {
context.moveBranchLabel();
context.deleteRedundantComments();
context.deleteStatements();
}
}
}
private static class MergeWithDefaultBranchFix implements LocalQuickFix {
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.duplicate.branches.in.switch.merge.with.default.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
FixContext context = new FixContext();
if (context.prepare(descriptor.getStartElement(), Branch::isDefault)) {
context.moveBranchLabel();
context.deleteRedundantComments();
context.deleteStatements();
}
}
}
private static class DeleteRedundantBranchFix implements LocalQuickFix {
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getName() {
return InspectionsBundle.message("inspection.duplicate.branches.in.switch.redundant.fix.name");
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("inspection.duplicate.branches.in.switch.redundant.fix.family.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
FixContext context = new FixContext();
if (context.prepare(descriptor.getStartElement(), Branch::isDefault)) {
context.deleteBranchLabel();
context.deleteStatements();
}
}
}
static class FixContext {
private Branch myBranchToDelete;
private Branch myBranchToMergeWith;
private List<PsiElement> myBranchPrefixToMove;
private PsiSwitchLabelStatement myLabelToMergeWith;
private Set<String> myCommentsToMergeWith;
private PsiElement myNextFromLabelToMergeWith;
private boolean prepare(PsiElement startElement, Predicate<Branch> shouldMergeWith) {
PsiSwitchStatement switchStatement = PsiTreeUtil.getParentOfType(startElement, PsiSwitchStatement.class);
if (switchStatement == null) return;
if (switchStatement == null) return false;
Branch branchToDelete = null;
List<Branch> candidateBranches = null;
for (List<Branch> branches : collectSameLengthBranches(switchStatement)) {
branchToDelete = ContainerUtil.find(branches, branch -> branch.myStatements[0] == startElement);
if (branchToDelete != null) {
myBranchToDelete = ContainerUtil.find(branches, branch -> branch.myStatements[0] == startElement);
if (myBranchToDelete != null) {
candidateBranches = branches;
break;
}
}
if (myBranchToDelete == null || candidateBranches == null) return false;
if (branchToDelete == null) return;
Branch branchToMergeWith = null;
for (Branch branch : candidateBranches) {
if (mySwitchLabelText.equals(branch.getSwitchLabelText()) && areDuplicates(branchToDelete, branch)) {
branchToMergeWith = branch;
if (shouldMergeWith.test(branch) && areDuplicates(myBranchToDelete, branch)) {
myBranchToMergeWith = branch;
break;
}
}
if (branchToMergeWith == null) return;
if (myBranchToMergeWith == null) return false;
List<PsiElement> branchPrefixToMove = branchToDelete.getBranchPrefix();
if (branchPrefixToMove.isEmpty()) return;
myBranchPrefixToMove = myBranchToDelete.getBranchPrefix();
if (myBranchPrefixToMove.isEmpty()) return false;
PsiSwitchLabelStatement labelToMergeWith =
PsiTreeUtil.getPrevSiblingOfType(branchToMergeWith.myStatements[0], PsiSwitchLabelStatement.class);
if (labelToMergeWith == null) return;
myLabelToMergeWith = PsiTreeUtil.getPrevSiblingOfType(myBranchToMergeWith.myStatements[0], PsiSwitchLabelStatement.class);
if (myLabelToMergeWith == null) return false;
PsiElement oldNextElement = PsiTreeUtil.skipWhitespacesForward(labelToMergeWith);
myNextFromLabelToMergeWith = PsiTreeUtil.skipWhitespacesForward(myLabelToMergeWith);
PsiElement firstElementToMove = branchPrefixToMove.get(0);
PsiElement lastElementToMove = branchPrefixToMove.get(branchPrefixToMove.size() - 1);
labelToMergeWith.getParent().addRangeAfter(firstElementToMove, lastElementToMove, labelToMergeWith);
myCommentsToMergeWith = ContainerUtil.set(myBranchToMergeWith.myCommentTexts);
return true;
}
void moveBranchLabel() {
PsiElement firstElementToMove = myBranchPrefixToMove.get(0);
PsiElement lastElementToMove = myBranchPrefixToMove.get(myBranchPrefixToMove.size() - 1);
PsiElement moveTarget = myLabelToMergeWith;
if (myLabelToMergeWith.isDefaultCase()) {
PsiElement prevElement = PsiTreeUtil.skipWhitespacesAndCommentsBackward(myLabelToMergeWith);
if (prevElement != null) moveTarget = prevElement;
}
moveTarget.getParent().addRangeAfter(firstElementToMove, lastElementToMove, moveTarget);
firstElementToMove.getParent().deleteChildRange(firstElementToMove, lastElementToMove);
}
Set<String> commentsToMergeWith = ContainerUtil.set(branchToMergeWith.myCommentTexts);
deleteRedundantComments(labelToMergeWith.getNextSibling(), oldNextElement, commentsToMergeWith);
void deleteStatements() {
CommentTracker tracker = new CommentTracker();
PsiStatement[] statementsToDelete = branchToDelete.getStatementsToDelete();
PsiStatement[] statementsToDelete = myBranchToDelete.getStatementsToDelete();
for (PsiStatement statement : statementsToDelete) {
PsiTreeUtil.processElements(statement, child -> {
if (isRedundantComment(commentsToMergeWith, child)) {
if (isRedundantComment(myCommentsToMergeWith, child)) {
tracker.markUnchanged(child);
}
return true;
@@ -255,13 +343,13 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
tracker.deleteAndRestoreComments(statementsToDelete[statementsToDelete.length - 1]);
}
private static void deleteRedundantComments(@Nullable PsiElement startElement,
@Nullable PsiElement stopElement,
@NotNull Set<String> existingComments) {
void deleteRedundantComments() {
List<PsiElement> redundantComments = new ArrayList<>();
for (PsiElement element = startElement; element != null && element != stopElement; element = element.getNextSibling()) {
for (PsiElement element = myLabelToMergeWith.getNextSibling();
element != null && element != myNextFromLabelToMergeWith;
element = element.getNextSibling()) {
PsiTreeUtil.processElements(element, child -> {
if (isRedundantComment(existingComments, child)) {
if (isRedundantComment(myCommentsToMergeWith, child)) {
redundantComments.add(child);
}
return true;
@@ -278,11 +366,37 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
}
return false;
}
void deleteBranchLabel() {
List<PsiElement> toDelete = new ArrayList<>();
CommentTracker tracker = new CommentTracker();
for (PsiElement element : myBranchPrefixToMove) {
if (element instanceof PsiWhiteSpace) {
continue;
}
toDelete.add(element);
PsiTreeUtil.processElements(element, child -> {
if (isRedundantComment(myCommentsToMergeWith, child)) {
tracker.markUnchanged(child);
}
return true;
});
}
int size = toDelete.size();
if (size != 0) {
for (int i = 0; i < size - 1; i++) {
tracker.delete(toDelete.get(i));
}
tracker.deleteAndRestoreComments(toDelete.get(size - 1));
}
}
}
private static class Branch {
private final PsiStatement[] myStatements;
private final String[] myCommentTexts;
private final boolean myIsDefault;
private final boolean myIsSimpleExit;
private final boolean myCanFallThrough;
@@ -298,6 +412,7 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
}
myStatements = statementList.toArray(PsiStatement.EMPTY_ARRAY);
myCommentTexts = commentTexts;
myIsDefault = calculateIsDefault(statementList.get(0));
}
@Nullable
@@ -317,6 +432,10 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
return myStatements.length;
}
boolean isDefault() {
return myIsDefault;
}
@Nullable
String getSwitchLabelText() {
PsiSwitchLabelStatement switchLabel = null;
@@ -330,10 +449,12 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
if (switchLabel.isDefaultCase()) {
return PsiKeyword.DEFAULT;
}
//todo support multi-value label
PsiExpression value = switchLabel.getCaseValue();
if (value != null) {
return PsiKeyword.CASE + ' ' + value.getText();
PsiExpressionList caseValues = switchLabel.getCaseValues();
if (caseValues != null) {
PsiExpression[] expressions = caseValues.getExpressions();
if (expressions.length != 0) {
return PsiKeyword.CASE + ' ' + expressions[0].getText();
}
}
}
return null;
@@ -346,7 +467,7 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
List<PsiElement> getBranchPrefix() {
List<PsiElement> result = new ArrayList<>();
for (PsiElement element = myStatements[0].getPrevSibling();
element != null && (element instanceof PsiSwitchLabelStatement || !(element instanceof PsiStatement));
element != null && !isLeftBrace(element) && (element instanceof PsiSwitchLabelStatement || !(element instanceof PsiStatement));
element = element.getPrevSibling()) {
result.add(element);
}
@@ -354,6 +475,10 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
return result;
}
private static boolean isLeftBrace(PsiElement element) {
return element instanceof PsiJavaToken && JavaTokenType.LBRACE.equals(((PsiJavaToken)element).getTokenType());
}
PsiStatement[] getStatementsToDelete() {
PsiStatement nextStatement = PsiTreeUtil.getNextSiblingOfType(myStatements[myStatements.length - 1], PsiStatement.class);
if (isBreakWithoutLabel(nextStatement)) {
@@ -405,6 +530,17 @@ public class DuplicateBranchesInSwitchInspection extends LocalInspectionTool {
}
return false;
}
private static boolean calculateIsDefault(PsiStatement statement) {
for (PsiElement element = PsiTreeUtil.getPrevSiblingOfType(statement, PsiStatement.class);
element instanceof PsiSwitchLabelStatement;
element = PsiTreeUtil.getPrevSiblingOfType(element, PsiStatement.class)) {
if (((PsiSwitchLabelStatement)element).isDefaultCase()) {
return true;
}
}
return false;
}
}
private static class Comments {
@@ -2,11 +2,11 @@ class C {
int foo(int n, boolean b) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">if(b) {
if(b) {
return bar("A");
} else {
break;
}</weak_warning>
}
case 2:
if(b) {
return bar("B");
@@ -2,12 +2,12 @@ class C {
void foo(int n, boolean b) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">if(b) {
if(b) {
bar("A");
} else {
bar("z");
}
bar("o");</weak_warning>
bar("o");
break;
case 2:
if(b) {
@@ -4,8 +4,8 @@ class C {
for (int i = 0; i < n; i++) {
switch (i % 4) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">s += i;
continue;</weak_warning>
s += i;
continue;
case 2:
continue;
case 3:
@@ -0,0 +1,16 @@
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
bar("A");
break;
case 1:
<weak_warning descr="Branch in 'switch' statement is a duplicate of the default branch">bar("A");</weak_warning>
break;
}
}
void bar(String s){}
}
@@ -2,7 +2,7 @@ class C {
void foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
bar("A");
case 2:
break;
case 3:
@@ -4,7 +4,7 @@ enum T {
int foo(T t) {
switch (t) {
case A:
<weak_warning descr="Duplicate branch in 'switch' statement">return t.ordinal(); // comment 1</weak_warning>
return t.ordinal(); // comment 1
case B:
<weak_warning descr="Duplicate branch in 'switch' statement">return t.ordinal();</weak_warning>
@@ -2,7 +2,7 @@ class C {
void foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
bar("A");
break;
case 2:
bar("B");
@@ -2,7 +2,7 @@ class C {
String foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">return "A";</weak_warning>
return "A";
case 2:
return "B";
case 3:
@@ -7,7 +7,7 @@ enum C {
switch (c) {
case ORIGINAL_CODE_WITH_COMMENT:
/* comment 1 */
<weak_warning descr="Duplicate branch in 'switch' statement">return "A";</weak_warning>
return "A";
case THE_SAME_CODE_WITH_DIFFERENT_COMMENT:
/* comment 2 */
return "A";
@@ -2,7 +2,7 @@ class C {
void foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
bar("A");
break;
case 2:
bar("B");
@@ -2,7 +2,7 @@ class C {
void foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
bar("A");
break;
case 2:
bar("B");
@@ -10,7 +10,7 @@ class C {
case 3:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
break;
default:
case 4:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
break;
}
@@ -0,0 +1,19 @@
class C {
void foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Branch in 'switch' statement is a duplicate of the default branch">bar("A");</weak_warning>
break;
case 2:
bar("B");
break;
case 3:
<weak_warning descr="Branch in 'switch' statement is a duplicate of the default branch">bar("A");</weak_warning>
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -2,7 +2,7 @@ class C {
String foo(int n) {
switch (n) {
case 1:
<weak_warning descr="Duplicate branch in 'switch' statement">throw new IllegalArgumentException("A");</weak_warning>
throw new IllegalArgumentException("A");
case 2:
throw new IllegalStateException("A");
case 3:
@@ -3,7 +3,7 @@ class C {
switch (n) {
case 1:
case 2:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
bar("A");
break;
case 3:
<weak_warning descr="Duplicate branch in 'switch' statement">bar("A");</weak_warning>
@@ -0,0 +1,16 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
/*comment 2*/
bar("A");
break;/*comment 1*/
/*comment 3*/
}
}
void bar(String s){}
}
@@ -0,0 +1,15 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 1:
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,14 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,14 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,15 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 1:
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,15 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 1:
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -3,7 +3,7 @@ class C {
void foo(int n) {
switch (n) {
case 1:
default:
case 4:
bar("A");
break;
case 2:
@@ -0,0 +1,18 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 3:
bar("A");
break;
case 1:
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,21 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
/*comment 2*/
bar("A");
break;
/*comment 1*/
case 1:
/*comment 2*/
bar("A");<caret>
/*comment 3*/
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,17 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
default:
bar("A");
break;
case 1:
bar("A");<caret>
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,17 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 1:
bar("A");<caret>
break;
case 2:
bar("B");
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,17 @@
// "Delete redundant 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 1:
bar("A");<caret>
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,17 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 1:
bar("A");<caret>
break;
case 2:
bar("B");
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -0,0 +1,17 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 2:
bar("B");
break;
case 1:
bar("A");<caret>
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -11,7 +11,7 @@ class C {
case 3:
bar("A");
break;
default:
case 4:
bar("A");<caret>
break;
}
@@ -0,0 +1,20 @@
// "Merge with the default 'switch' branch" "GENERIC_ERROR_OR_WARNING"
class C {
void foo(int n) {
switch (n) {
case 1:
bar("A");<caret>
break;
case 2:
bar("B");
break;
case 3:
bar("A");
break;
default:
bar("A");
break;
}
}
void bar(String s){}
}
@@ -28,6 +28,8 @@ class DuplicateBranchesInSwitchTest : LightCodeInsightFixtureTestCase() {
fun testNoLastBreak() = doTest()
fun testFallThroughToBreak() = doTest()
fun testThreeDuplicates() = doTest()
fun testThreeDuplicatesDefault() = doTest()
fun testDuplicateAfterDefault() = doTest()
fun testTwoCaseLabels() = doTest()
fun testComplexBranches() = doTest()
fun testBreakWithLabel() = doTest()
@@ -1043,6 +1043,10 @@ inspection.duplicate.branches.in.switch.display.name=Duplicate branches in 'swit
inspection.duplicate.branches.in.switch.message=Duplicate branch in 'switch' statement
inspection.duplicate.branches.in.switch.fix.family.name=Merge duplicate branches of 'switch' statement
inspection.duplicate.branches.in.switch.fix.name=Merge with ''{0}:''
inspection.duplicate.branches.in.switch.redundant.message=Branch in 'switch' statement is a duplicate of the default branch
inspection.duplicate.branches.in.switch.redundant.fix.family.name=Delete redundant branches of 'switch' statement
inspection.duplicate.branches.in.switch.redundant.fix.name=Delete redundant 'switch' branch
inspection.duplicate.branches.in.switch.merge.with.default.fix.name=Merge with the default 'switch' branch
inspection.switch.labeled.rule.can.be.code.block.display.name=Labeled switch rule can have code block
inspection.switch.labeled.rule.can.be.code.block.expression.message=Labeled rule's result expression can be wrapped with code block