IDEA-91011 (Cast to concrete class shall have an option to ignore equals() method)

This commit is contained in:
Bas Leijdekkers
2012-09-11 17:41:33 +02:00
parent 166300a242
commit 03c3ce3d68
7 changed files with 131 additions and 46 deletions
@@ -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
@@ -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);
}
}
}
@@ -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) {
@@ -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.
<!-- tooltip end -->
<p>
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.
<p>
Use the second checkbox below to have this inspection ignore casts inside <code>equals()</code> methods.
<p>
<small>Powered by InspectionGadgets</small>
</body>
@@ -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<CastToConcreteClass> aClass = CastToConcreteClass.class;
final CastToConcreteClass c3 = aClass.cast(o);
}
}
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>CastToConcreteClass.java</file>
<line>18</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cast to a concrete class</problem_class>
<description>Cast to concrete class &lt;code&gt;CastToConcreteClass&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>CastToConcreteClass.java</file>
<line>19</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cast to a concrete class</problem_class>
<description>Cast to concrete class &lt;code&gt;CastToConcreteClass&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>CastToConcreteClass.java</file>
<line>21</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Cast to a concrete class</problem_class>
<description>Cast to concrete class &lt;code&gt;CastToConcreteClass&lt;/code&gt; #loc</description>
</problem>
</problems>
@@ -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);
}
}