mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
first stab at an inspection to detect identical 'catch' blocks in a 'try' statement (quickfix coming soon)
This commit is contained in:
@@ -34,6 +34,7 @@ import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.IntArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@@ -49,12 +50,12 @@ public class DuplicatesFinder {
|
||||
private final List<? extends PsiVariable> myOutputParameters;
|
||||
private final List<PsiElement> myPatternAsList;
|
||||
private boolean myMultipleExitPoints = false;
|
||||
private final ReturnValue myReturnValue;
|
||||
@Nullable private final ReturnValue myReturnValue;
|
||||
|
||||
public DuplicatesFinder(PsiElement[] pattern,
|
||||
InputVariables parameters,
|
||||
ReturnValue returnValue,
|
||||
List<? extends PsiVariable> outputParameters
|
||||
@Nullable ReturnValue returnValue,
|
||||
@NotNull List<? extends PsiVariable> outputParameters
|
||||
) {
|
||||
myReturnValue = returnValue;
|
||||
LOG.assertTrue(pattern.length > 0);
|
||||
@@ -109,6 +110,14 @@ public class DuplicatesFinder {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Match isDuplicate(PsiElement element, boolean ignoreParameterTypes) {
|
||||
annotatePattern();
|
||||
Match match = isDuplicateFragment(element, ignoreParameterTypes);
|
||||
deannotatePattern();
|
||||
return match;
|
||||
}
|
||||
|
||||
private void annotatePattern() {
|
||||
for (final PsiElement patternComponent : myPattern) {
|
||||
patternComponent.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@@ -146,7 +155,7 @@ public class DuplicatesFinder {
|
||||
private void findPatternOccurrences(List<Match> array, PsiElement scope) {
|
||||
PsiElement[] children = scope.getChildren();
|
||||
for (PsiElement child : children) {
|
||||
final Match match = isDuplicateFragment(child);
|
||||
final Match match = isDuplicateFragment(child, false);
|
||||
if (match != null) {
|
||||
array.add(match);
|
||||
continue;
|
||||
@@ -157,7 +166,7 @@ public class DuplicatesFinder {
|
||||
|
||||
|
||||
@Nullable
|
||||
private Match isDuplicateFragment(PsiElement candidate) {
|
||||
private Match isDuplicateFragment(PsiElement candidate, boolean ignoreParameterTypes) {
|
||||
if (PsiTreeUtil.isAncestor(myPattern[0], candidate, false)) return null;
|
||||
PsiElement sibling = candidate;
|
||||
ArrayList<PsiElement> candidates = new ArrayList<PsiElement>();
|
||||
@@ -188,7 +197,7 @@ public class DuplicatesFinder {
|
||||
}
|
||||
|
||||
}
|
||||
final Match match = new Match(candidates.get(0), candidates.get(candidates.size() - 1));
|
||||
final Match match = new Match(candidates.get(0), candidates.get(candidates.size() - 1), ignoreParameterTypes);
|
||||
for (int i = 0; i < myPattern.length; i++) {
|
||||
if (!matchPattern(myPattern[i], candidates.get(i), candidates, match)) return null;
|
||||
}
|
||||
|
||||
@@ -48,16 +48,18 @@ public final class Match {
|
||||
private final PsiElement myMatchStart;
|
||||
private final PsiElement myMatchEnd;
|
||||
private final Map<PsiVariable, List<PsiElement>> myParameterValues = new HashMap<PsiVariable, List<PsiElement>>();
|
||||
private final Map<PsiVariable, ArrayList<PsiElement>> myParameterOccurences = new HashMap<PsiVariable, ArrayList<PsiElement>>();
|
||||
private final Map<PsiVariable, ArrayList<PsiElement>> myParameterOccurrences = new HashMap<PsiVariable, ArrayList<PsiElement>>();
|
||||
private final Map<PsiElement, PsiElement> myDeclarationCorrespondence = new HashMap<PsiElement, PsiElement>();
|
||||
private ReturnValue myReturnValue = null;
|
||||
private Ref<PsiExpression> myInstanceExpression = null;
|
||||
private final Map<PsiVariable, PsiType> myChangedParams = new HashMap<PsiVariable, PsiType>();
|
||||
private final boolean myIgnoreParameterTypes;
|
||||
|
||||
Match(PsiElement start, PsiElement end) {
|
||||
Match(PsiElement start, PsiElement end, boolean ignoreParameterTypes) {
|
||||
LOG.assertTrue(start.getParent() == end.getParent());
|
||||
myMatchStart = start;
|
||||
myMatchEnd = end;
|
||||
myIgnoreParameterTypes = ignoreParameterTypes;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,14 +137,14 @@ public final class Match {
|
||||
myChangedParams.put(psiVariable, new PsiEllipsisType(parameterType));
|
||||
}
|
||||
} else {
|
||||
if (!parameterType.isAssignableFrom(type)) return false; //todo
|
||||
if (!myIgnoreParameterTypes && !parameterType.isAssignableFrom(type)) return false; //todo
|
||||
}
|
||||
}
|
||||
final List<PsiElement> values = new ArrayList<PsiElement>();
|
||||
values.add(value);
|
||||
myParameterValues.put(psiVariable, values);
|
||||
final ArrayList<PsiElement> elements = new ArrayList<PsiElement>();
|
||||
myParameterOccurences.put(psiVariable, elements);
|
||||
myParameterOccurrences.put(psiVariable, elements);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -157,7 +159,7 @@ public final class Match {
|
||||
currentValue.add(value);
|
||||
}
|
||||
}
|
||||
myParameterOccurences.get(psiVariable).add(value);
|
||||
myParameterOccurrences.get(psiVariable).add(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1843,4 +1843,6 @@ package.dot.html.delete.command=package.html deletion
|
||||
package.dot.html.may.be.package.info.convert.quickfix=Convert to package-info.java
|
||||
package.dot.html.convert.command=package.html to package-info.java conversion
|
||||
choose.super.class.to.ignore=Choose class
|
||||
ignore.anonymous.inner.classes=Ignore anonymous inner classes
|
||||
ignore.anonymous.inner.classes=Ignore anonymous inner classes
|
||||
try.with.identical.catches.display.name=Identical 'catch' branches in 'try' statement
|
||||
try.with.identical.catches.problem.descriptor=Identical 'catch' branches in 'try' statement #loc
|
||||
|
||||
@@ -24,7 +24,9 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.siyeh.ig.ui.FormattedTextFieldMacFix;
|
||||
@@ -283,4 +285,9 @@ public abstract class BaseInspection extends BaseJavaLocalInspectionTool {
|
||||
inspectionGadgetsPlugin = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TextRange getProblemTextRange(PsiElement element) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.siyeh.ig;
|
||||
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -167,7 +168,13 @@ public abstract class BaseInspectionVisitor extends JavaElementVisitor{
|
||||
fix.setOnTheFly(onTheFly);
|
||||
}
|
||||
final String description = inspection.buildErrorString(infos);
|
||||
holder.registerProblem(location, description, fixes);
|
||||
TextRange range = inspection.getProblemTextRange(location);
|
||||
if (range != null) {
|
||||
holder.registerProblem(location, range, description, fixes);
|
||||
}
|
||||
else {
|
||||
holder.registerProblem(location, description, fixes);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -833,6 +833,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent,
|
||||
m_inspectionClasses.add(ThrowFromFinallyBlockInspection.class);
|
||||
m_inspectionClasses.add(TooBroadCatchInspection.class);
|
||||
m_inspectionClasses.add(TooBroadThrowsInspection.class);
|
||||
m_inspectionClasses.add(TryWithIdenticalCatchesInspection.class);
|
||||
m_inspectionClasses.add(UncheckedExceptionClassInspection.class);
|
||||
m_inspectionClasses.add(UnusedCatchParameterInspection.class);
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.errorhandling;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.extractMethod.InputVariables;
|
||||
import com.intellij.refactoring.util.duplicates.DuplicatesFinder;
|
||||
import com.intellij.refactoring.util.duplicates.Match;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class TryWithIdenticalCatchesInspection extends BaseInspection {
|
||||
@NotNull
|
||||
@Override
|
||||
protected String buildErrorString(Object... infos) {
|
||||
return InspectionGadgetsBundle.message("try.with.identical.catches.problem.descriptor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseInspectionVisitor buildVisitor() {
|
||||
return new TryWithIdenticalCatchesVisitor();
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return InspectionGadgetsBundle.message("try.with.identical.catches.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextRange getProblemTextRange(PsiElement element) {
|
||||
if (element instanceof PsiCatchSection) {
|
||||
PsiJavaToken rParenth = ((PsiCatchSection)element).getRParenth();
|
||||
if (rParenth != null) {
|
||||
return new TextRange(0, rParenth.getTextOffset() + 1 - element.getTextOffset());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class TryWithIdenticalCatchesVisitor extends BaseInspectionVisitor {
|
||||
@Override
|
||||
public void visitTryStatement(PsiTryStatement statement) {
|
||||
super.visitTryStatement(statement);
|
||||
if (!PsiUtil.isLanguageLevel7OrHigher(statement)) {
|
||||
return;
|
||||
}
|
||||
PsiCatchSection[] catchSections = statement.getCatchSections();
|
||||
boolean[] duplicates = new boolean[catchSections.length];
|
||||
for (int i = 0; i < catchSections.length; i++) {
|
||||
if (duplicates[i]) continue;
|
||||
InputVariables inputVariables = new InputVariables(Collections.singletonList(catchSections[i].getParameter()),
|
||||
statement.getProject(),
|
||||
new LocalSearchScope(catchSections[i].getCatchBlock()),
|
||||
false);
|
||||
DuplicatesFinder finder = new DuplicatesFinder(new PsiElement[] { catchSections [i].getCatchBlock() },
|
||||
inputVariables, null, Collections.<PsiVariable>emptyList());
|
||||
for (int j = 0; j < catchSections.length; j++) {
|
||||
if (i == j || duplicates[j]) continue;
|
||||
Match match = finder.isDuplicate(catchSections[j].getCatchBlock(), true);
|
||||
if (match != null) {
|
||||
registerError(catchSections[j]);
|
||||
duplicates[i] = true;
|
||||
duplicates[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports identical 'catch' sections in 'try' blocks under JDK 7. A quickfix is provided to collapse the sections into
|
||||
a multi-catch section.
|
||||
<small>Powered by InspectionGadgets</small>
|
||||
</body>
|
||||
</html>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.siyeh.igtest.errorhandling.try_identical_catches;
|
||||
|
||||
class TryIdenticalCatches {
|
||||
public void notIdentical() {
|
||||
try {
|
||||
|
||||
}
|
||||
catch(NumberFormatException e) {
|
||||
log(e);
|
||||
}
|
||||
catch(RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void identical(boolean value) {
|
||||
try {
|
||||
if (value) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
else {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
catch(ClassNotFoundException cnfe) {
|
||||
log(cnfe);
|
||||
}
|
||||
<warning descr="Identical 'catch' branches in 'try' statement">catch(NumberFormatException nfe)</warning> {
|
||||
log(nfe);
|
||||
}
|
||||
}
|
||||
|
||||
private void log(Exception e) {
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.errorhandling;
|
||||
|
||||
import com.intellij.openapi.application.PluginPathManager;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class TryWithIdenticalCatchesTest extends LightCodeInsightFixtureTestCase {
|
||||
public void test() {
|
||||
myFixture.enableInspections(TryWithIdenticalCatchesInspection.class);
|
||||
myFixture.configureByFile("com/siyeh/igtest/errorhandling/try_identical_catches/TryIdenticalCatches.java");
|
||||
myFixture.checkHighlighting(true, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return PluginPathManager.getPluginHomePathRelative("InspectionGadgets") + "/test";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user