make incompatible interface inspection less expensive in the presence of classes with a lot of subclasses

This commit is contained in:
Bas Leijdekkers
2013-07-23 14:03:44 +02:00
parent 96a522ee43
commit 723e37e97f
6 changed files with 119 additions and 63 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2007 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2013 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,15 +27,13 @@ public class CastToIncompatibleInterfaceInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"casting.to.incompatible.interface.display.name");
return InspectionGadgetsBundle.message("casting.to.incompatible.interface.display.name");
}
@Override
@NotNull
public String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
"casting.to.incompatible.interface.problem.descriptor");
return InspectionGadgetsBundle.message("casting.to.incompatible.interface.problem.descriptor");
}
@Override
@@ -43,12 +41,10 @@ public class CastToIncompatibleInterfaceInspection extends BaseInspection {
return new CastToIncompatibleInterfaceVisitor();
}
private static class CastToIncompatibleInterfaceVisitor
extends BaseInspectionVisitor {
private static class CastToIncompatibleInterfaceVisitor extends BaseInspectionVisitor {
@Override
public void visitTypeCastExpression(
@NotNull PsiTypeCastExpression expression) {
public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
final PsiTypeElement castTypeElement = expression.getCastType();
if (castTypeElement == null) {
@@ -69,20 +65,14 @@ public class CastToIncompatibleInterfaceInspection extends BaseInspection {
}
final PsiClassType operandClassType = (PsiClassType)operandType;
final PsiClass castClass = castClassType.resolve();
if (castClass == null) {
return;
}
if (!castClass.isInterface()) {
if (castClass == null || !castClass.isInterface()) {
return;
}
final PsiClass operandClass = operandClassType.resolve();
if (operandClass == null) {
if (operandClass == null || operandClass.isInterface()) {
return;
}
if (operandClass.isInterface()) {
return;
}
if (InheritanceUtil.existsMutualSubclass(operandClass, castClass)) {
if (InheritanceUtil.existsMutualSubclass(operandClass, castClass, isOnTheFly())) {
return;
}
registerError(castTypeElement);
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2007 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2013 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,21 +22,18 @@ import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.InheritanceUtil;
import org.jetbrains.annotations.NotNull;
public class InstanceofIncompatibleInterfaceInspection
extends BaseInspection {
public class InstanceofIncompatibleInterfaceInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"instanceof.with.incompatible.interface.display.name");
return InspectionGadgetsBundle.message("instanceof.with.incompatible.interface.display.name");
}
@Override
@NotNull
public String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
"instanceof.with.incompatible.interface.problem.descriptor");
return InspectionGadgetsBundle.message("instanceof.with.incompatible.interface.problem.descriptor");
}
@Override
@@ -44,12 +41,10 @@ public class InstanceofIncompatibleInterfaceInspection
return new InstanceofIncompatibleInterfaceVisitor();
}
private static class InstanceofIncompatibleInterfaceVisitor
extends BaseInspectionVisitor {
private static class InstanceofIncompatibleInterfaceVisitor extends BaseInspectionVisitor {
@Override
public void visitInstanceOfExpression(
@NotNull PsiInstanceOfExpression expression) {
public void visitInstanceOfExpression(@NotNull PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
final PsiTypeElement castTypeElement = expression.getCheckType();
if (castTypeElement == null) {
@@ -67,20 +62,14 @@ public class InstanceofIncompatibleInterfaceInspection
}
final PsiClassType operandClassType = (PsiClassType)operandType;
final PsiClass castClass = castClassType.resolve();
if (castClass == null) {
return;
}
if (!castClass.isInterface()) {
if (castClass == null || !castClass.isInterface()) {
return;
}
final PsiClass operandClass = operandClassType.resolve();
if (operandClass == null) {
if (operandClass == null || operandClass.isInterface()) {
return;
}
if (operandClass.isInterface()) {
return;
}
if (InheritanceUtil.existsMutualSubclass(operandClass, castClass)) {
if (InheritanceUtil.existsMutualSubclass(operandClass, castClass, isOnTheFly())) {
return;
}
registerError(castTypeElement);
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2013 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,18 +32,18 @@ public class InheritanceUtil {
private InheritanceUtil() {}
public static boolean existsMutualSubclass(PsiClass class1, final PsiClass class2) {
public static boolean existsMutualSubclass(PsiClass class1, final PsiClass class2, final boolean avoidExpensiveProcessing) {
if (class1 instanceof PsiTypeParameter) {
final PsiClass[] superClasses = class1.getSupers();
for (PsiClass superClass : superClasses) {
if (!existsMutualSubclass(superClass, class2)) {
if (!existsMutualSubclass(superClass, class2, avoidExpensiveProcessing)) {
return false;
}
}
return true;
}
if (class2 instanceof PsiTypeParameter) {
return existsMutualSubclass(class2, class1);
return existsMutualSubclass(class2, class1, avoidExpensiveProcessing);
}
final String className = class1.getQualifiedName();
@@ -54,18 +54,26 @@ public class InheritanceUtil {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(class2Name)) {
return true;
}
if (class1.isInheritor(class2, true) ||
class2.isInheritor(class1, true)) {
if (class1.isInheritor(class2, true) || class2.isInheritor(class1, true)) {
return true;
}
final SearchScope scope = GlobalSearchScope.allScope(class1.getProject());
final Query<PsiClass> search = ClassInheritorsSearch.search(class1, scope, true, true);
return !search.forEach(new Processor<PsiClass>() {
final boolean[] result = new boolean[1];
search.forEach(new Processor<PsiClass>() {
int count = 0;
@Override
public boolean process(PsiClass inheritor) {
return !inheritor.equals(class2) && !inheritor.isInheritor(class2, true);
count++;
if (inheritor.equals(class2) || inheritor.isInheritor(class2, true) || (avoidExpensiveProcessing && count > 20)) {
result[0] = true;
return false;
}
return true;
}
});
return result[0];
}
public static boolean hasImplementation(PsiClass aClass) {
@@ -1,17 +0,0 @@
package com.siyeh.igtest.bugs;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
public class CastToIncompatibleInterfaceInspection {
public void foo()
{
List list = (List) new HashMap();
List list2 = (List) new ArrayList();
if(new HashMap() instanceof List)
{
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.ig.bugs;
import com.intellij.codeInspection.LocalInspectionTool;
import com.siyeh.ig.LightInspectionTestCase;
/**
* @author Bas Leijdekkers
*/
public class CastToIncompatibleInterfaceInspectionTest extends LightInspectionTestCase {
public void testSimple() {
doTest("class X { " +
" I list = (/*Cast to incompatible interface 'I'*/I/**/) new C(); " +
"}" +
"interface I {}" +
"class C {}");
}
public void testHashMap() {
doTest("import java.util.HashMap;" +
"import java.util.List;" +
"class X {" +
" List l = (/*Cast to incompatible interface 'List'*/List/**/) new HashMap();" +
"}");
}
@Override
protected LocalInspectionTool getInspection() {
return new CastToIncompatibleInterfaceInspection();
}
}
@@ -0,0 +1,40 @@
/*
* 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.ig.bugs;
import com.intellij.codeInspection.LocalInspectionTool;
import com.siyeh.ig.LightInspectionTestCase;
/**
* @author Bas Leijdekkers
*/
public class InstanceofIncompatibleInterfaceInspectionTest extends LightInspectionTestCase {
public void testHashMap() {
doTest("import java.util.HashMap;" +
"import java.util.List;" +
"class X {" +
" void m() {" +
" if(new HashMap() instanceof /*'instanceof' incompatible interface 'List'*/List/**/);" +
" }" +
"}");
}
@Override
protected LocalInspectionTool getInspection() {
return new InstanceofIncompatibleInterfaceInspection();
}
}