From fa5b5eab0750cb93968a5951e9fbf8eaee034e36 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Tue, 27 Nov 2018 16:24:17 +0300 Subject: [PATCH] Java: Convert switch labeled rule expression to a block (IDEA-202622) --- java/java-impl/src/META-INF/JavaPlugin.xml | 5 + ...chLabeledRuleCanBeCodeBlockInspection.java | 104 ++++++++++++++++++ .../SwitchLabeledRuleCanBeCodeBlock.html | 7 ++ .../InExpression.java | 12 ++ .../InStatement.java | 14 +++ .../afterCallInExpression.java | 11 ++ .../afterCallInStatement.java | 13 +++ .../afterConstInExpression.java | 11 ++ .../afterThrowInExpression.java | 11 ++ .../afterThrowInStatement.java | 13 +++ .../beforeCallInExpression.java | 9 ++ .../beforeCallInStatement.java | 11 ++ .../beforeConstInExpression.java | 9 ++ .../beforeThrowInExpression.java | 9 ++ .../beforeThrowInStatement.java | 11 ++ .../SwitchLabeledRuleCanBeCodeBlockFixTest.kt | 20 ++++ .../SwitchLabeledRuleCanBeCodeBlockTest.kt | 36 ++++++ .../src/messages/InspectionsBundle.properties | 8 +- 18 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 java/java-impl/src/com/intellij/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockInspection.java create mode 100644 java/java-impl/src/inspectionDescriptions/SwitchLabeledRuleCanBeCodeBlock.html create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InStatement.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInStatement.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterConstInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInStatement.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInStatement.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeConstInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInExpression.java create mode 100644 java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInStatement.java create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockFixTest.kt create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockTest.kt diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 561811adffff..554753bbaa36 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -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" /> + diff --git a/java/java-impl/src/com/intellij/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockInspection.java b/java/java-impl/src/com/intellij/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockInspection.java new file mode 100644 index 000000000000..cefb7deb2977 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockInspection.java @@ -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() + " }"); + } + } +} diff --git a/java/java-impl/src/inspectionDescriptions/SwitchLabeledRuleCanBeCodeBlock.html b/java/java-impl/src/inspectionDescriptions/SwitchLabeledRuleCanBeCodeBlock.html new file mode 100644 index 000000000000..9bd0b7f192b2 --- /dev/null +++ b/java/java-impl/src/inspectionDescriptions/SwitchLabeledRuleCanBeCodeBlock.html @@ -0,0 +1,7 @@ + + +Reports rules of switch expression or enhanced switch statement which can be converted to a code block. + +

New in 2019.1

+ + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InExpression.java new file mode 100644 index 000000000000..4e0262c4ad85 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InExpression.java @@ -0,0 +1,12 @@ +class C { + String foo(int n) { + return switch (n) { + case 1 -> Integer.toString(n); + case 2 -> { break Integer.toString(n); } + case 3 -> throw new RuntimeException(); + case 4 -> { throw new RuntimeException(); } + case 5 -> { break "a";} + default -> "b"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InStatement.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InStatement.java new file mode 100644 index 000000000000..2d34b1ee6144 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlock/InStatement.java @@ -0,0 +1,14 @@ +class C { + String foo(int n) { + String s; + switch (n) { + case 1 -> s = Integer.toString(n); + case 2 -> { s = Integer.toString(n); } + case 3 -> throw new RuntimeException(); + case 4 -> { throw new RuntimeException(); } + case 5 -> { s = "a"; } + default -> s = "b"; + }; + return s; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInExpression.java new file mode 100644 index 000000000000..91f7eda7843c --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInExpression.java @@ -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"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInStatement.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInStatement.java new file mode 100644 index 000000000000..9c07afc6064a --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterCallInStatement.java @@ -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; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterConstInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterConstInExpression.java new file mode 100644 index 000000000000..466f79f67b45 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterConstInExpression.java @@ -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"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInExpression.java new file mode 100644 index 000000000000..6ba89babefcd --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInExpression.java @@ -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"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInStatement.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInStatement.java new file mode 100644 index 000000000000..dd90c0e9ebb1 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/afterThrowInStatement.java @@ -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; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInExpression.java new file mode 100644 index 000000000000..fafdc4fe88e5 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInExpression.java @@ -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) { + case 1 -> /*1*/Integer.toString(/*2*/n);/*3*/ + default -> "b"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInStatement.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInStatement.java new file mode 100644 index 000000000000..5c9399fb57e7 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeCallInStatement.java @@ -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) { + case 1 -> /*1*/s = Integer.toString(/*2*/n);/*3*/ + default -> s = "b"; + }; + return s; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeConstInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeConstInExpression.java new file mode 100644 index 000000000000..fcdcdc26b08d --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeConstInExpression.java @@ -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) { + case 1 -> /*1*/ "a"; /*2*/ + default -> "b"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInExpression.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInExpression.java new file mode 100644 index 000000000000..6ad1d1439fe5 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInExpression.java @@ -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) { + case 1 -> /*1*/ throw new RuntimeException(); /*2*/ + default -> "b"; + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInStatement.java b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInStatement.java new file mode 100644 index 000000000000..7a1aba992b48 --- /dev/null +++ b/java/java-tests/testData/inspection/switchLabeledRuleCanBeCodeBlockFix/beforeThrowInStatement.java @@ -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) { + case 1 -> /*1*/ throw /*2*/new RuntimeException(); /*3*/ + default -> s = "b"; + }; + return s; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockFixTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockFixTest.kt new file mode 100644 index 000000000000..19bf64a67c23 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockFixTest.kt @@ -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 = arrayOf(SwitchLabeledRuleCanBeCodeBlockInspection()) + + override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_12 + + override fun getBasePath() = "/inspection/switchLabeledRuleCanBeCodeBlockFix" +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockTest.kt new file mode 100644 index 000000000000..28a0fefb0730 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/enhancedSwitch/SwitchLabeledRuleCanBeCodeBlockTest.kt @@ -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") + } +} \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 7dcabed928c6..1d883943d6d1 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -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}:'' \ No newline at end of file +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 \ No newline at end of file