Java inspection: convert the intention for "Split Multi-Catch into Separate Catch Blocks" into an INFORMATION-level inspection (IDEA-157727)

This commit is contained in:
Pavel Dolgov
2016-06-29 19:55:49 +03:00
parent 20a2b0c121
commit eb937bc8d7
20 changed files with 226 additions and 180 deletions
@@ -2559,6 +2559,10 @@
key="split.try.with.multiple.resources.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.exceptions.SplitTryWithMultipleResourcesInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="SplitMultiCatch" bundle="com.siyeh.InspectionGadgetsBundle"
key="split.try.with.multiple.resources.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" enabledByDefault="true" level="INFORMATION"
implementationClass="com.siyeh.ig.exceptions.SplitMultiCatchInspection"/>
<!--group.names.threading.issues-->
<localInspection groupPath="Java" language="JAVA" suppressId="AccessToNonThreadSafeStaticField" shortName="AccessToNonThreadSafeStaticFieldFromInstance"
@@ -2189,3 +2189,5 @@ replace.method.ref.with.lambda.name=Replace method reference with lambda
replace.method.ref.with.lambda.descriptor=Replace method reference with lambda
split.try.with.multiple.resources.name=Split Try Statement with Multiple Resources
split.try.with.multiple.resources.descriptor=Split 'try' statement with multiple resources
split.multi.catch.name=Split Multi-Catch into Separate Catch Blocks
split.multi.catch.descriptor=Split multi-catch into separate 'catch' blocks
@@ -0,0 +1,141 @@
/*
* Copyright 2011 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.exceptions;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.util.ObjectUtils.assertNotNull;
public class SplitMultiCatchInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("split.multi.catch.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("split.multi.catch.descriptor");
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new SplitMultiCatchVisitor();
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return new SplitMultiCatchFix();
}
private static void doFixImpl(@NotNull PsiElement element) throws IncorrectOperationException {
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiCatchSection)) {
return;
}
final PsiCatchSection catchSection = (PsiCatchSection)parent;
final PsiElement grandParent = catchSection.getParent();
if (!(grandParent instanceof PsiTryStatement)) {
return;
}
final PsiParameter parameter = catchSection.getParameter();
if (parameter == null) {
return;
}
final PsiType type = parameter.getType();
if (!(type instanceof PsiDisjunctionType)) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject());
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(element.getProject());
for (PsiType disjunction : ((PsiDisjunctionType)type).getDisjunctions()) {
final PsiCatchSection copy = (PsiCatchSection)catchSection.copy();
final PsiTypeElement typeElement = assertNotNull(assertNotNull(copy.getParameter()).getTypeElement());
final PsiTypeElement newTypeElement = factory.createTypeElementFromText(disjunction.getCanonicalText(true), catchSection);
final PsiElement replaced = typeElement.replace(newTypeElement);
grandParent.addBefore(copy, catchSection);
styleManager.shortenClassReferences(replaced);
}
catchSection.delete();
}
private static boolean isAcceptable(@NotNull PsiElement element) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiCatchSection) {
final PsiType type = ((PsiCatchSection)parent).getCatchType();
return type instanceof PsiDisjunctionType;
}
return false;
}
private static class SplitMultiCatchVisitor extends BaseInspectionVisitor {
@Override
public void visitParameter(PsiParameter parameter) {
super.visitParameter(parameter);
if (isAcceptable(parameter)) {
registerError(parameter);
}
}
@Override
public void visitKeyword(PsiKeyword keyword) {
super.visitKeyword(keyword);
if (isAcceptable(keyword)) {
registerError(keyword);
}
}
}
private static class SplitMultiCatchFix extends InspectionGadgetsFix {
@Nls
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("split.multi.catch.descriptor");
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
doFixImpl(descriptor.getPsiElement());
}
}
}
@@ -0,0 +1,6 @@
<html>
<body>
This inspection splits a multi-catch section into separate <b>catch</b> blocks.
<br><br>
</body>
</html>
@@ -0,0 +1,10 @@
import java.io.*;
public class AtCodeBlock {
void foo() {
try {
Reader reader = new FileReader("");
} catch (IndexOutOfBoundsException | FileNotFoundException e) {<caret>
}
}
}
@@ -0,0 +1,16 @@
import java.io.IOException;
import java.net.*;
public class AtParameter {
void f() {
try {
throw new NoRouteToHostException();
} catch (NoRouteToHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,12 @@
import java.io.IOException;
import java.net.*;
public class AtParameter {
void f() {
try {
throw new NoRouteToHostException();
} catch (NoRouteToHostException | SocketException | IOException<caret> e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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.fixes.exceptions;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.IGQuickFixesTestCase;
import com.siyeh.ig.exceptions.SplitMultiCatchInspection;
public class SplitMultiCatchFixTest extends IGQuickFixesTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.enableInspections(new SplitMultiCatchInspection());
myDefaultHint = InspectionGadgetsBundle.message("split.multi.catch.descriptor");
myRelativePath = "exceptions/split_multi_catch";
}
public void testSimple() { doTest(); }
public void testTypeAnno() { doTest(); }
public void testAtParameter() { doTest(); }
public void testAtCodeBlock() { assertQuickfixNotAvailable(); }
}
@@ -481,11 +481,6 @@
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
<categoryKey>intention.category.try.statements</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.exceptions.SplitMultiCatchIntention</className>
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
<categoryKey>intention.category.try.statements</categoryKey>
</intentionAction>
<intentionAction>
<className>com.siyeh.ipp.exceptions.ReplaceArmWithTryFinallyIntention</className>
<bundleName>com.siyeh.IntentionPowerPackBundle</bundleName>
@@ -152,8 +152,6 @@ expand.to.normal.annotation.intention.name=Add 'value=' to annotation attribute
annotate.overridden.methods.intention.family.name=Annotate overriding methods and their parameters
annotate.overridden.methods.intention.method.name=Annotate overriding methods as ''{0}''
annotate.overridden.methods.intention.parameters.name=Annotate same parameter of overriding methods as ''{0}''
split.multi.catch.intention.name=Split multi-catch into separate 'catch' blocks
split.multi.catch.intention.family.name=Split Multi-Catch into Separate Catch Blocks
replace.arm.with.try.finally.intention.name=Replace 'try-with-resources' with 'try finally'
replace.arm.with.try.finally.intention.family.name=Replace Try-With-Resources with Try-Finally
merge.nested.try.statements.intention.name=Merge nested 'try' statements
@@ -1,35 +0,0 @@
/*
* Copyright 2011-2013 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.ipp.exceptions;
import com.intellij.psi.*;
import com.siyeh.ipp.base.PsiElementPredicate;
class MulticatchPredicate implements PsiElementPredicate {
public boolean satisfiedBy(PsiElement element) {
if (element instanceof PsiCodeBlock) {
return false;
}
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiCatchSection)) {
return false;
}
final PsiCatchSection catchSection = (PsiCatchSection)parent;
final PsiType type = catchSection.getCatchType();
return type instanceof PsiDisjunctionType;
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2011 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.ipp.exceptions;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ipp.base.Intention;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
import static com.intellij.util.ObjectUtils.assertNotNull;
public class SplitMultiCatchIntention extends Intention {
@NotNull
@Override
protected PsiElementPredicate getElementPredicate() {
return new MulticatchPredicate();
}
@Override
protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiCatchSection)) {
return;
}
final PsiCatchSection catchSection = (PsiCatchSection)parent;
final PsiElement grandParent = catchSection.getParent();
if (!(grandParent instanceof PsiTryStatement)) {
return;
}
final PsiParameter parameter = catchSection.getParameter();
if (parameter == null) {
return;
}
final PsiType type = parameter.getType();
if (!(type instanceof PsiDisjunctionType)) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject());
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(element.getProject());
for (PsiType disjunction : ((PsiDisjunctionType)type).getDisjunctions()) {
final PsiCatchSection copy = (PsiCatchSection)catchSection.copy();
final PsiTypeElement typeElement = assertNotNull(assertNotNull(copy.getParameter()).getTypeElement());
final PsiTypeElement newTypeElement = factory.createTypeElementFromText(disjunction.getCanonicalText(true), catchSection);
final PsiElement replaced = typeElement.replace(newTypeElement);
grandParent.addBefore(copy, catchSection);
styleManager.shortenClassReferences(replaced);
}
catchSection.delete();
}
}
@@ -1,16 +0,0 @@
import java.io.IOException;
import java.net.*;
public class X {
void f() {
try {
throw new NoRouteToHostException();
} catch (NoRouteToHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -1,12 +0,0 @@
import java.io.IOException;
import java.net.*;
public class X {
void f() {
try {
throw new NoRouteToHostException();
} <spot>catch</spot> (NoRouteToHostException | SocketException | IOException e) {
e.printStackTrace();
}
}
}
@@ -1,6 +0,0 @@
<html>
<body>
This intention splits a multi-catch section into separate <b>catch</b> blocks.
<br><br>
</body>
</html>
@@ -1,34 +0,0 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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.ipp.exceptions;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.IPPTestCase;
public class SplitMultiCatchIntentionTest extends IPPTestCase {
public void testSimple() { doTest(); }
public void testTypeAnno() { doTest(); }
@Override
protected String getIntentionName() {
return IntentionPowerPackBundle.message("split.multi.catch.intention.name");
}
@Override
protected String getRelativePath() {
return "exceptions/splitMultiCatch";
}
}