diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 9de82e4a9d56..5b20b359f9ea 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1932,6 +1932,7 @@ simplifiable.equals.expression.display.name=Unnecessary 'null' check before 'equ simplifiable.equals.expression.problem.descriptor=Unnecessary ''null'' check before ''{0}()'' call #loc simplifiable.equals.expression.quickfix=Flip ''.{0}()'' and remove unnecessary ''null'' check cast.to.concrete.class.option=Ignore casts to an abstract class type +cast.to.concrete.class.ignore.equals.option=Ignore in equals() instanceof.interfaces.option=Ignore instanceof abstract class instance.variable.of.concrete.class.option=Ignore instance fields whose type is an abstract class local.variable.of.concrete.class.option=Ignore local variables whose type is an abstract class diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java index 75e19fed6b7d..345a6e19c47a 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/CastToConcreteClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 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. @@ -15,43 +15,45 @@ */ package com.siyeh.ig.abstraction; -import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import javax.swing.JComponent; +import javax.swing.*; public class CastToConcreteClassInspection extends BaseInspection { @SuppressWarnings("PublicField") public boolean ignoreAbstractClasses = false; + @SuppressWarnings("PublicField") + public boolean ignoreInEquals = true; + @Override @NotNull public String getDisplayName() { - return InspectionGadgetsBundle.message( - "cast.to.concrete.class.display.name"); + return InspectionGadgetsBundle.message("cast.to.concrete.class.display.name"); } @Override @NotNull protected String buildErrorString(Object... infos) { - final PsiElement typeElement = (PsiElement)infos[0]; - return InspectionGadgetsBundle.message( - "cast.to.concrete.class.problem.descriptor", - typeElement.getText()); + final PsiType type= (PsiType)infos[0]; + return InspectionGadgetsBundle.message("cast.to.concrete.class.problem.descriptor", type.getPresentableText()); } @Override public JComponent createOptionsPanel() { - return new SingleCheckboxOptionsPanel( - InspectionGadgetsBundle.message( - "cast.to.concrete.class.option"), - this, "ignoreAbstractClasses"); + final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this); + panel.addCheckbox(InspectionGadgetsBundle.message("cast.to.concrete.class.option"), "ignoreAbstractClasses"); + panel.addCheckbox(InspectionGadgetsBundle.message("cast.to.concrete.class.ignore.equals.option"), "ignoreInEquals"); + return panel; } @Override @@ -59,49 +61,68 @@ public class CastToConcreteClassInspection extends BaseInspection { return new CastToConcreteClassVisitor(); } - private class CastToConcreteClassVisitor - extends BaseInspectionVisitor { + private class CastToConcreteClassVisitor extends BaseInspectionVisitor { @Override - public void visitTypeCastExpression( - @NotNull PsiTypeCastExpression expression) { + public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) { super.visitTypeCastExpression(expression); final PsiTypeElement typeElement = expression.getCastType(); if (typeElement == null) { return; } - if (!ConcreteClassUtil.typeIsConcreteClass(typeElement, - ignoreAbstractClasses)) { + if (!ConcreteClassUtil.typeIsConcreteClass(typeElement, ignoreAbstractClasses)) { return; } - registerError(typeElement, typeElement); + if (ignoreInEquals) { + final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class); + if (MethodUtils.isEquals(method)) { + return; + } + } + registerError(typeElement, typeElement.getType()); } @Override - public void visitMethodCallExpression( - PsiMethodCallExpression expression) { + public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - final PsiReferenceExpression methodExpression = - expression.getMethodExpression(); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); @NonNls final String referenceName = methodExpression.getReferenceName(); if (!"cast".equals(referenceName)) { return; } - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); - if (!(qualifier instanceof PsiClassObjectAccessExpression)) { + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier == null) { return; } - final PsiClassObjectAccessExpression classObjectAccessExpression = - (PsiClassObjectAccessExpression)qualifier; - final PsiTypeElement operand = - classObjectAccessExpression.getOperand(); - if (!ConcreteClassUtil.typeIsConcreteClass(operand, - ignoreAbstractClasses)) { + final PsiType type = qualifier.getType(); + if (!(type instanceof PsiClassType)) { return; } - registerMethodCallError(expression, operand); + final PsiClassType classType = (PsiClassType)type; + final PsiClass aClass = classType.resolve(); + if (aClass == null) { + return; + } + final String className = aClass.getQualifiedName(); + if (!CommonClassNames.JAVA_LANG_CLASS.equals(className)) { + return; + } + final PsiType[] parameters = classType.getParameters(); + if (parameters.length != 1) { + return; + } + final PsiType parameter = parameters[0]; + if (!ConcreteClassUtil.typeIsConcreteClass(parameter, ignoreAbstractClasses)) { + return; + } + if (ignoreInEquals) { + final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class); + if (MethodUtils.isEquals(method)) { + return; + } + } + registerMethodCallError(expression, parameter); } } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/ConcreteClassUtil.java b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/ConcreteClassUtil.java index 32d6c8eef81e..44554566a439 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/ConcreteClassUtil.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/ConcreteClassUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 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. @@ -21,30 +21,33 @@ import org.jetbrains.annotations.Nullable; class ConcreteClassUtil { - private ConcreteClassUtil() { - } + private ConcreteClassUtil() {} - public static boolean typeIsConcreteClass( - @Nullable PsiTypeElement typeElement, - boolean ignoreCastToAbstractClass) { + public static boolean typeIsConcreteClass(@Nullable PsiTypeElement typeElement, boolean ignoreCastToAbstractClass) { if (typeElement == null) { return false; } final PsiType type = typeElement.getType(); + return typeIsConcreteClass(type, ignoreCastToAbstractClass); + } + + public static boolean typeIsConcreteClass(@Nullable PsiType type, boolean ignoreCastToAbstractClass) { + if (type == null) { + return false; + } final PsiType baseType = type.getDeepComponentType(); if (!(baseType instanceof PsiClassType)) { return false; } - final PsiClass aClass = ((PsiClassType)baseType).resolve(); + final PsiClassType classType = (PsiClassType)baseType; + final PsiClass aClass = classType.resolve(); if (aClass == null) { return false; } - if (ignoreCastToAbstractClass && - aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + if (ignoreCastToAbstractClass && aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { return false; } - if (aClass.isInterface() || aClass.isEnum() || - aClass.isAnnotationType()) { + if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType()) { return false; } if (aClass instanceof PsiTypeParameter) { diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html index 069e856eb04d..585311f920e2 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CastToConcreteClass.html @@ -5,7 +5,9 @@ Such declarations may represent a failure of abstraction, and may make testing m Declarations whose classes come from system or third-party libraries will not be reported by this inspection.

-Use the checkbox below to have this inspection ignore casts to abstract classes. +Use the first checkbox below to have this inspection ignore casts to abstract classes. +

+Use the second checkbox below to have this inspection ignore casts inside equals() methods.

Powered by InspectionGadgets diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java new file mode 100644 index 000000000000..a4868a962999 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/CastToConcreteClass.java @@ -0,0 +1,23 @@ +package com.siyeh.igtest.abstraction.cast_to_concrete_class; + +class CastToConcreteClass { + + private String field; + + @Override + public boolean equals(Object obj) { + try { + CastToConcreteClass c = (CastToConcreteClass)obj; + return c.field.equals(field); + } catch (ClassCastException e) { + return false; + } + } + + void foo(Object o) { + CastToConcreteClass c = (CastToConcreteClass)o; + CastToConcreteClass c2 = CastToConcreteClass.class.cast(o); + final Class aClass = CastToConcreteClass.class; + final CastToConcreteClass c3 = aClass.cast(o); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml new file mode 100644 index 000000000000..76af465c4b12 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/abstraction/cast_to_concrete_class/expected.xml @@ -0,0 +1,23 @@ + + + + CastToConcreteClass.java + 18 + Cast to a concrete class + Cast to concrete class <code>CastToConcreteClass</code> #loc + + + + CastToConcreteClass.java + 19 + Cast to a concrete class + Cast to concrete class <code>CastToConcreteClass</code> #loc + + + + CastToConcreteClass.java + 21 + Cast to a concrete class + Cast to concrete class <code>CastToConcreteClass</code> #loc + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java new file mode 100644 index 000000000000..bd9e6e35ba9a --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/abstraction/CastToConcreteClassInspectionTest.java @@ -0,0 +1,12 @@ +package com.siyeh.ig.abstraction; + +import com.siyeh.ig.IGInspectionTestCase; + +public class CastToConcreteClassInspectionTest extends IGInspectionTestCase { + + public void test() throws Exception { + final CastToConcreteClassInspection tool = new CastToConcreteClassInspection(); + tool.ignoreInEquals = true; + doTest("com/siyeh/igtest/abstraction/cast_to_concrete_class", tool); + } +} \ No newline at end of file