IDEA-111465 ("Overly-strong type cast" inspection false negative in foreach)

This commit is contained in:
Bas Leijdekkers
2013-08-02 21:41:28 +02:00
parent 13c924df85
commit 67cce2234e
11 changed files with 115 additions and 107 deletions
@@ -27,8 +27,8 @@ magic.number.problem.descriptor=Magic number <code>#ref</code> #loc
magic.number.ignore.option=Ignore constants in 'hashCode()' methods
method.return.concrete.class.display.name=Method return of concrete class
method.return.concrete.class.problem.descriptor=Method returns a concrete class <code>#ref</code> #loc
overly.strong.type.cast.display.name=Overly-strong type cast
overly.strong.type.cast.weaken.quickfix=Weaken overly-strong cast
overly.strong.type.cast.display.name=Overly strong type cast
overly.strong.type.cast.weaken.quickfix=Weaken overly strong cast
concrete.class.method.parameter.display.name=Method parameter of concrete class
concrete.class.method.parameter.problem.descriptor=Parameter ''{0}'' of concrete class <code>#ref</code> #loc
public.method.not.in.interface.display.name='public' method not exposed in interface
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2010 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.
@@ -20,7 +20,6 @@ import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
@@ -41,8 +40,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message(
"overly.strong.type.cast.display.name");
return InspectionGadgetsBundle.message("overly.strong.type.cast.display.name");
}
@Override
@@ -50,17 +48,14 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
protected String buildErrorString(Object... infos) {
final PsiType expectedType = (PsiType)infos[0];
final String typeText = expectedType.getPresentableText();
return InspectionGadgetsBundle.message(
"overly.strong.type.cast.problem.descriptor", typeText);
return InspectionGadgetsBundle.message("overly.strong.type.cast.problem.descriptor", typeText);
}
@Override
@Nullable
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel(
InspectionGadgetsBundle.message(
"overly.strong.type.cast.ignore.in.matching.instanceof.option"),
this, "ignoreInMatchingInstanceof");
return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("overly.strong.type.cast.ignore.in.matching.instanceof.option"),
this, "ignoreInMatchingInstanceof");
}
@Override
@@ -73,21 +68,17 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
@Override
@NotNull
public String getName() {
return InspectionGadgetsBundle.message(
"overly.strong.type.cast.weaken.quickfix");
return InspectionGadgetsBundle.message("overly.strong.type.cast.weaken.quickfix");
}
@Override
public void doFix(Project project, ProblemDescriptor descriptor)
throws IncorrectOperationException {
public void doFix(Project project, ProblemDescriptor descriptor) {
final PsiElement castTypeElement = descriptor.getPsiElement();
final PsiTypeCastExpression expression =
(PsiTypeCastExpression)castTypeElement.getParent();
final PsiTypeCastExpression expression = (PsiTypeCastExpression)castTypeElement.getParent();
if (expression == null) {
return;
}
final PsiType expectedType =
ExpectedTypeUtils.findExpectedType(expression, true);
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
if (expectedType == null) {
return;
}
@@ -96,9 +87,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
return;
}
@NonNls
final String newExpression =
'(' + expectedType.getCanonicalText() + ')' +
operand.getText();
final String newExpression = '(' + expectedType.getCanonicalText() + ')' + operand.getText();
replaceExpressionAndShorten(expression, newExpression);
}
}
@@ -108,12 +97,10 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
return new OverlyStrongTypeCastVisitor();
}
private class OverlyStrongTypeCastVisitor
extends BaseInspectionVisitor {
private class OverlyStrongTypeCastVisitor extends BaseInspectionVisitor {
@Override
public void visitTypeCastExpression(
@NotNull PsiTypeCastExpression expression) {
public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
final PsiExpression operand = expression.getOperand();
if (operand == null) {
@@ -127,8 +114,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
if (type == null) {
return;
}
final PsiType expectedType =
ExpectedTypeUtils.findExpectedType(expression, true);
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
if (expectedType == null) {
return;
}
@@ -140,7 +126,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
return;
}
if (expectedType.isAssignableFrom(operandType)) {
//then it's redundant, and caught by the built-in exception
//then it's redundant, and caught by the built-in inspection
return;
}
if (isTypeParameter(expectedType)) {
@@ -153,19 +139,15 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
return;
}
}
if (type instanceof PsiPrimitiveType ||
expectedType instanceof PsiPrimitiveType) {
if (type instanceof PsiPrimitiveType || expectedType instanceof PsiPrimitiveType) {
return;
}
if (PsiPrimitiveType.getUnboxedType(type) != null ||
PsiPrimitiveType.getUnboxedType(expectedType) != null) {
if (PsiPrimitiveType.getUnboxedType(type) != null || PsiPrimitiveType.getUnboxedType(expectedType) != null) {
return;
}
if (expectedType instanceof PsiClassType) {
final PsiClassType expectedClassType =
(PsiClassType)expectedType;
final PsiClassType expectedRawType =
expectedClassType.rawType();
final PsiClassType expectedClassType = (PsiClassType)expectedType;
final PsiClassType expectedRawType = expectedClassType.rawType();
if (type.equals(expectedRawType)) {
return;
}
@@ -180,8 +162,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
return;
}
}
if (ignoreInMatchingInstanceof &&
InstanceOfUtils.hasAgreeingInstanceof(expression)) {
if (ignoreInMatchingInstanceof && InstanceOfUtils.hasAgreeingInstanceof(expression)) {
return;
}
final PsiTypeElement castTypeElement = expression.getCastType();
@@ -197,10 +178,7 @@ public class OverlyStrongTypeCastInspection extends BaseInspection {
}
final PsiClassType classType = (PsiClassType)type;
final PsiClass aClass = classType.resolve();
if (aClass == null) {
return false;
}
return aClass instanceof PsiTypeParameter;
return aClass != null && aClass instanceof PsiTypeParameter;
}
}
}
@@ -278,6 +278,29 @@ public class ExpectedTypeUtils {
expectedType = PsiType.BOOLEAN;
}
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
final PsiExpression iteratedValue = statement.getIteratedValue();
if (iteratedValue == null) {
expectedType = null;
return;
}
final PsiType iteratedValueType = iteratedValue.getType();
if (!(iteratedValueType instanceof PsiClassType)) {
expectedType = null;
return;
}
final PsiClassType classType = (PsiClassType)iteratedValueType;
final PsiType[] parameters = classType.getParameters();
final PsiClass iterableClass = ClassUtils.findClass(CommonClassNames.JAVA_LANG_ITERABLE, statement);
if (iterableClass == null) {
expectedType = null;
}
else {
expectedType = JavaPsiFacade.getElementFactory(statement.getProject()).createType(iterableClass, parameters);
}
}
@Override
public void visitIfStatement(@NotNull PsiIfStatement statement) {
expectedType = PsiType.BOOLEAN;
@@ -1,14 +0,0 @@
package com.siyeh.igtest.abstraction;
import com.siyeh.igtest.abstraction2.SuperClass2;
public class CastWeakenTest extends SuperClass2 {
public static void main(String[] args) {
Object a = new CastWeakenTest();
((CastWeakenTest)a).getHandle();
}
protected Object getHandle() {
return super.getHandle();
}
}
@@ -1,23 +1,28 @@
package com.siyeh.igtest.abstraction;
import com.siyeh.igtest.abstraction2.SubClass;
import com.siyeh.igtest.abstraction2.SuperClass;
import com.siyeh.igtest.abstraction2.SubClass2;
package com.siyeh.igtest.abstraction.overly_strong_type_cast;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.AbstractList;
import java.lang.reflect.Array;
import java.io.Serializable;
interface TestInter{}
public class OverlyStrongTypeCastInspection
public class OverlyStrongTypeCast
{
void iterate(Object o) {
for (Object object : (ArrayList) o) {}
for (String s : (ArrayList<String>) o) {}
}
void optional(Object foo) {
if (foo instanceof OverlyStrongTypeCastInspection) {
((OverlyStrongTypeCastInspection)foo).hashCode();
if (foo instanceof SubClass2) {
((SubClass2)foo).doSmth();
}
((SubClass2)foo).doSmth();
}
public static void main(String[] args)
@@ -47,15 +52,16 @@ public class OverlyStrongTypeCastInspection
((SubClass2)testSub).doSmth();
}
}
interface Task<T extends Serializable> extends Serializable {}
interface ConcreteTask<T extends Serializable> extends Task<T> {
void aMethod();
class SuperClass{
protected void doSmth(){
}
}
class SubClass extends SuperClass{
public void doSmth(){
}
}
class SubClass2 extends SubClass{
public void doSmth(){
super.doSmth();
}
}
class Test {
public void main(String[] argv) {
Task<?> t = new Task<String>() {};
if (t instanceof ConcreteTask)
// We get a warning here that the cast can be weakened to "ConcreteTask<? extends Serializable>".
((ConcreteTask<? extends String>)t).aMethod();
}
}
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>OverlyStrongTypeCast.java</file>
<line>17</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Overly strong type cast</problem_class>
<description>Cast to &lt;code&gt;ArrayList&lt;/code&gt; can be weakened to 'Iterable' #loc</description>
</problem>
<problem>
<file>OverlyStrongTypeCast.java</file>
<line>18</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Overly strong type cast</problem_class>
<description>Cast to &lt;code&gt;ArrayList&lt;String&gt;&lt;/code&gt; can be weakened to 'Iterable&lt;String&gt;' #loc</description>
</problem>
<problem>
<file>OverlyStrongTypeCast.java</file>
<line>25</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Overly strong type cast</problem_class>
<description>Cast to &lt;code&gt;SubClass2&lt;/code&gt; can be weakened to 'SuperClass' #loc</description>
</problem>
<problem>
<file>OverlyStrongTypeCast.java</file>
<line>31</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Overly strong type cast</problem_class>
<description>Cast to &lt;code&gt;ArrayList&lt;/code&gt; can be weakened to 'AbstractList' #loc</description>
</problem>
</problems>
@@ -1,6 +0,0 @@
package com.siyeh.igtest.abstraction2;
public class SubClass extends SuperClass{
public void doSmth(){
}
}
@@ -1,8 +0,0 @@
package com.siyeh.igtest.abstraction2;
public class SubClass2 extends SubClass{
public void doSmth(){
super.doSmth();
}
}
@@ -1,6 +0,0 @@
package com.siyeh.igtest.abstraction2;
public class SuperClass{
protected void doSmth(){
}
}
@@ -1,7 +0,0 @@
package com.siyeh.igtest.abstraction2;
public class SuperClass2 {
protected Object getHandle() {
return null;
};
}
@@ -0,0 +1,12 @@
package com.siyeh.ig.abstraction;
import com.siyeh.ig.IGInspectionTestCase;
public class OverlyStrongTypeCastInspectionTest extends IGInspectionTestCase {
public void test() throws Exception {
final OverlyStrongTypeCastInspection tool = new OverlyStrongTypeCastInspection();
tool.ignoreInMatchingInstanceof = true;
doTest("com/siyeh/igtest/abstraction/overly_strong_type_cast", tool);
}
}