Java: Convert switch labeled rule expression to a block (IDEA-202622)

This commit is contained in:
Pavel Dolgov
2018-11-28 15:09:59 +03:00
parent 0d96d61986
commit fa5b5eab07
18 changed files with 313 additions and 1 deletions
@@ -940,6 +940,11 @@
groupKey="group.names.language.level.specific.issues.and.migration.aids12" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="INFORMATION"
implementationClass="com.intellij.codeInspection.EnhancedSwitchMigrationInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="SwitchLabeledRuleCanBeCodeBlock"
key="inspection.switch.labeled.rule.can.be.code.block.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="INFORMATION"
implementationClass="com.intellij.codeInspection.enhancedSwitch.SwitchLabeledRuleCanBeCodeBlockInspection" />
<globalInspection groupPath="Java" language="JAVA" shortName="EmptyMethod" displayName="Empty method" groupKey="group.names.declaration.redundancy" enabledByDefault="true" groupBundle="messages.InspectionsBundle"
level="WARNING" implementationClass="com.intellij.codeInspection.emptyMethod.EmptyMethodInspection"/>
@@ -0,0 +1,104 @@
// 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.codeInspection.enhancedSwitch;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import static com.intellij.codeInspection.InspectionsBundle.message;
/**
* @author Pavel.Dolgov
*/
public class SwitchLabeledRuleCanBeCodeBlockInspection extends LocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_12_PREVIEW)) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitSwitchLabeledRuleStatement(PsiSwitchLabeledRuleStatement statement) {
super.visitSwitchLabeledRuleStatement(statement);
PsiSwitchBlock switchBlock = statement.getEnclosingSwitchBlock();
PsiStatement body = statement.getBody();
if (switchBlock != null && (body instanceof PsiExpressionStatement || body instanceof PsiThrowStatement)) {
if (switchBlock instanceof PsiSwitchExpression && body instanceof PsiExpressionStatement) {
registerProblem(statement, true);
}
else {
registerProblem(statement, false);
}
}
}
private void registerProblem(PsiSwitchLabeledRuleStatement statement, boolean isExpressionResult) {
holder.registerProblem(ObjectUtils.notNull(ObjectUtils.tryCast(statement.getFirstChild(), PsiKeyword.class), statement),
message(isExpressionResult ? "inspection.switch.labeled.rule.can.be.code.block.expression.message"
: "inspection.switch.labeled.rule.can.be.code.block.statement.message"),
new WrapWithCodeBlockFix(isExpressionResult));
}
};
}
private static class WrapWithCodeBlockFix implements LocalQuickFix {
private final String myMessage;
WrapWithCodeBlockFix(boolean isExpressionResult) {
myMessage = message(isExpressionResult ? "inspection.switch.labeled.rule.can.be.code.block.fix.expression.name"
: "inspection.switch.labeled.rule.can.be.code.block.fix.statement.name");
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getFamilyName() {
return myMessage;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
if (element instanceof PsiKeyword) {
element = element.getParent();
if (element instanceof PsiSwitchLabeledRuleStatement) {
PsiSwitchLabeledRuleStatement rule = (PsiSwitchLabeledRuleStatement)element;
PsiSwitchBlock switchBlock = rule.getEnclosingSwitchBlock();
PsiStatement body = rule.getBody();
if (switchBlock instanceof PsiSwitchExpression && body instanceof PsiExpressionStatement) {
wrapExpression((PsiExpressionStatement)body);
}
else if (body != null) {
wrapStatement(body);
}
}
}
}
private static void wrapExpression(PsiExpressionStatement expressionStatement) {
PsiExpression expression = expressionStatement.getExpression();
CommentTracker tracker = new CommentTracker();
tracker.markUnchanged(expression);
tracker.replaceAndRestoreComments(expressionStatement, "{ break " + expression.getText() + "; }");
}
private static void wrapStatement(@NotNull PsiStatement statement) {
CommentTracker tracker = new CommentTracker();
tracker.markUnchanged(statement);
tracker.replaceAndRestoreComments(statement, "{ " + statement.getText() + " }");
}
}
}
@@ -0,0 +1,7 @@
<html>
<body>
Reports rules of <code>switch</code> expression or enhanced <code>switch</code> statement which can be converted to a code block.
<!-- tooltip end -->
<p><small>New in 2019.1</small></p>
</body>
</html>
@@ -0,0 +1,12 @@
class C {
String foo(int n) {
return switch (n) {
<warning descr="Labeled rule's result can be wrapped with code block">case</warning> 1 -> Integer.toString(n);
case 2 -> { break Integer.toString(n); }
<warning descr="Labeled rule's statement can be wrapped with code block">case</warning> 3 -> throw new RuntimeException();
case 4 -> { throw new RuntimeException(); }
case 5 -> { break "a";}
<warning descr="Labeled rule's result can be wrapped with code block">default</warning> -> "b";
};
}
}
@@ -0,0 +1,14 @@
class C {
String foo(int n) {
String s;
switch (n) {
<warning descr="Labeled rule's statement can be wrapped with code block">case</warning> 1 -> s = Integer.toString(n);
case 2 -> { s = Integer.toString(n); }
<warning descr="Labeled rule's statement can be wrapped with code block">case</warning> 3 -> throw new RuntimeException();
case 4 -> { throw new RuntimeException(); }
case 5 -> { s = "a"; }
<warning descr="Labeled rule's statement can be wrapped with code block">default</warning> -> s = "b";
};
return s;
}
}
@@ -0,0 +1,11 @@
// "Wrap labeled rule's result with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
case 1 -> /*1*/{
break Integer.toString(/*2*/n);
}/*3*/
default -> "b";
};
}
}
@@ -0,0 +1,13 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
String s;
switch (n) {
case 1 -> /*1*/{
s = Integer.toString(/*2*/n);
}/*3*/
default -> s = "b";
};
return s;
}
}
@@ -0,0 +1,11 @@
// "Wrap labeled rule's result with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
case 1 -> /*1*/ {
break "a";
} /*2*/
default -> "b";
};
}
}
@@ -0,0 +1,11 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
case 1 -> /*1*/ {
throw new RuntimeException(); /*2*/
}
default -> "b";
};
}
}
@@ -0,0 +1,13 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
String s;
switch (n) {
case 1 -> /*1*/ {
throw /*2*/new RuntimeException(); /*3*/
}
default -> s = "b";
};
return s;
}
}
@@ -0,0 +1,9 @@
// "Wrap labeled rule's result with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
<caret>case 1 -> /*1*/Integer.toString(/*2*/n);/*3*/
default -> "b";
};
}
}
@@ -0,0 +1,11 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
String s;
switch (n) {
<caret>case 1 -> /*1*/s = Integer.toString(/*2*/n);/*3*/
default -> s = "b";
};
return s;
}
}
@@ -0,0 +1,9 @@
// "Wrap labeled rule's result with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
<caret>case 1 -> /*1*/ "a"; /*2*/
default -> "b";
};
}
}
@@ -0,0 +1,9 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
return switch (n) {
<caret>case 1 -> /*1*/ throw new RuntimeException(); /*2*/
default -> "b";
};
}
}
@@ -0,0 +1,11 @@
// "Wrap labeled rule's statement with code block" "GENERIC_ERROR_OR_WARNING"
class C {
String foo(int n) {
String s;
switch (n) {
<caret>case 1 -> /*1*/ throw /*2*/new RuntimeException(); /*3*/
default -> s = "b";
};
return s;
}
}
@@ -0,0 +1,20 @@
// 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.java.codeInspection.enhancedSwitch
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.enhancedSwitch.SwitchLabeledRuleCanBeCodeBlockInspection
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
/**
* @author Pavel.Dolgov
*/
class SwitchLabeledRuleCanBeCodeBlockFixTest : LightQuickFixParameterizedTestCase() {
override fun configureLocalInspectionTools(): Array<LocalInspectionTool> = arrayOf(SwitchLabeledRuleCanBeCodeBlockInspection())
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_12
override fun getBasePath() = "/inspection/switchLabeledRuleCanBeCodeBlockFix"
}
@@ -0,0 +1,36 @@
// 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.java.codeInspection.enhancedSwitch
import com.intellij.JavaTestUtil
import com.intellij.codeHighlighting.HighlightDisplayLevel
import com.intellij.codeInsight.daemon.HighlightDisplayKey
import com.intellij.codeInspection.enhancedSwitch.SwitchLabeledRuleCanBeCodeBlockInspection
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
/**
* @author Pavel.Dolgov
*/
class SwitchLabeledRuleCanBeCodeBlockTest : LightCodeInsightFixtureTestCase() {
val inspection = SwitchLabeledRuleCanBeCodeBlockInspection()
override fun setUp() {
super.setUp()
myFixture.enableInspections(inspection)
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
currentProfile.setErrorLevel(HighlightDisplayKey.find(inspection.shortName), HighlightDisplayLevel.WARNING, project)
}
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_12
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/switchLabeledRuleCanBeCodeBlock"
fun testInExpression() = doTest()
fun testInStatement() = doTest()
private fun doTest() {
myFixture.testHighlighting("${getTestName(false)}.java")
}
}
@@ -1035,4 +1035,10 @@ inspection.replace.with.enhanced.switch.statement.fix.name=Replace with enhanced
inspection.duplicate.branches.in.switch.display.name=Duplicate branches in 'switch' statement
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.fix.name=Merge with ''{0}:''
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 can be wrapped with code block
inspection.switch.labeled.rule.can.be.code.block.statement.message=Labeled rule's statement can be wrapped with code block
inspection.switch.labeled.rule.can.be.code.block.fix.expression.name=Wrap labeled rule's result with code block
inspection.switch.labeled.rule.can.be.code.block.fix.statement.name=Wrap labeled rule's statement with code block