IDEA-111571 (boxing and unboxing with a cast not reported by inspections)

This commit is contained in:
Bas Leijdekkers
2013-08-05 17:22:51 +02:00
parent b025546359
commit 20dbc2b67c
7 changed files with 98 additions and 21 deletions
@@ -111,7 +111,7 @@ public class AutoBoxingInspection extends BaseInspection {
@Override
public void doFix(Project project, ProblemDescriptor descriptor) {
final PsiExpression expression = (PsiExpression)descriptor.getPsiElement();
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true);
if (expectedType == null) {
return;
}
@@ -146,7 +146,13 @@ public class AutoBoxingInspection extends BaseInspection {
else {
newExpression = classToConstruct + ".valueOf(" + expressionText + ')';
}
replaceExpression(expression, newExpression);
final PsiElement parent = expression.getParent();
if (parent instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)parent;
replaceExpression(typeCastExpression, newExpression);
} else {
replaceExpression(expression, newExpression);
}
}
private static boolean shortcutReplace(PsiExpression expression, String classToConstruct) {
@@ -303,7 +309,7 @@ public class AutoBoxingInspection extends BaseInspection {
if (boxedType == null) {
return;
}
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true);
if (expectedType == null || ClassUtils.isPrimitive(expectedType)) {
return;
}
@@ -37,9 +37,7 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class AutoUnboxingInspection extends BaseInspection {
@@ -59,6 +57,19 @@ public class AutoUnboxingInspection extends BaseInspection {
s_unboxingMethods.put("char", "charValue");
}
@NonNls static final Set<String> unboxableTypes = new HashSet(9);
static {
unboxableTypes.add(CommonClassNames.JAVA_LANG_BYTE);
unboxableTypes.add(CommonClassNames.JAVA_LANG_SHORT);
unboxableTypes.add(CommonClassNames.JAVA_LANG_INTEGER);
unboxableTypes.add(CommonClassNames.JAVA_LANG_LONG);
unboxableTypes.add(CommonClassNames.JAVA_LANG_FLOAT);
unboxableTypes.add(CommonClassNames.JAVA_LANG_DOUBLE);
unboxableTypes.add(CommonClassNames.JAVA_LANG_BOOLEAN);
unboxableTypes.add(CommonClassNames.JAVA_LANG_CHARACTER);
unboxableTypes.add(CommonClassNames.JAVA_LANG_OBJECT);
}
@Override
@NotNull
public String getDisplayName() {
@@ -131,7 +142,7 @@ public class AutoUnboxingInspection extends BaseInspection {
if (type == null) {
return;
}
final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
final PsiPrimitiveType unboxedType = (PsiPrimitiveType)ExpectedTypeUtils.findExpectedType(expression, false, true);
if (unboxedType == null) {
return;
}
@@ -140,7 +151,11 @@ public class AutoUnboxingInspection extends BaseInspection {
final PsiElementFactory factory = psiFacade.getElementFactory();
final PsiElement parent = expression.getParent();
final String expressionText = expression.getText();
if (parent instanceof PsiPrefixExpression && !unboxedType.equalsToText("boolean")) {
if (parent instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)parent;
replaceExpression(typeCastExpression, newExpressionText);
}
else if (parent instanceof PsiPrefixExpression && !unboxedType.equalsToText("boolean")) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)parent;
final IElementType tokenType = prefixExpression.getOperationTokenType();
if (JavaTokenType.PLUSPLUS.equals(tokenType)) {
@@ -205,7 +220,7 @@ public class AutoUnboxingInspection extends BaseInspection {
}
}
private static String buildNewExpressionText(PsiExpression expression, PsiType unboxedType) {
private static String buildNewExpressionText(PsiExpression expression, PsiPrimitiveType unboxedType) {
final String unboxedTypeText = unboxedType.getCanonicalText();
final String expressionText = expression.getText();
final String boxMethodName = s_unboxingMethods.get(unboxedTypeText);
@@ -225,6 +240,10 @@ public class AutoUnboxingInspection extends BaseInspection {
return argument.getText();
}
}
final PsiType type = expression.getType();
if (type != null && type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
return "((" + unboxedType.getBoxedTypeName() + ')' + expressionText + ")." + boxMethodName + "()";
}
return expressionText + '.' + boxMethodName + "()";
}
@@ -365,21 +384,23 @@ public class AutoUnboxingInspection extends BaseInspection {
if (TypeConversionUtil.isPrimitiveAndNotNull(expressionType)) {
return;
}
final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(expressionType);
if (unboxedType == null) {
if (!isUnboxable(expressionType)) {
return;
}
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
if (expectedType == null) {
return;
}
if (!TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) {
return;
}
if (!expectedType.isAssignableFrom(unboxedType)) {
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true);
if (expectedType == null || !TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) {
return;
}
registerError(expression, expression);
}
private static boolean isUnboxable(PsiType type) {
if (!(type instanceof PsiClassType)) {
return false;
}
final PsiClassType classType = (PsiClassType)type;
final PsiClass aClass = classType.resolve();
return aClass != null && unboxableTypes.contains(aClass.getQualifiedName());
}
}
}
@@ -33,6 +33,10 @@ public class ExpectedTypeUtils {
@Nullable
public static PsiType findExpectedType(@NotNull PsiExpression expression, boolean calculateTypeForComplexReferences) {
return findExpectedType(expression, calculateTypeForComplexReferences, false);
}
public static PsiType findExpectedType(PsiExpression expression, boolean calculateTypeForComplexReferences, boolean reportCasts) {
PsiElement context = expression.getParent();
PsiExpression wrappedExpression = expression;
while (context instanceof PsiParenthesizedExpression) {
@@ -42,7 +46,7 @@ public class ExpectedTypeUtils {
if (context == null) {
return null;
}
final ExpectedTypeVisitor visitor = new ExpectedTypeVisitor(wrappedExpression, calculateTypeForComplexReferences);
final ExpectedTypeVisitor visitor = new ExpectedTypeVisitor(wrappedExpression, calculateTypeForComplexReferences, reportCasts);
context.accept(visitor);
return visitor.getExpectedType();
}
@@ -92,11 +96,13 @@ public class ExpectedTypeUtils {
@NotNull private final PsiExpression wrappedExpression;
private final boolean calculateTypeForComplexReferences;
private final boolean reportCasts;
private PsiType expectedType = null;
ExpectedTypeVisitor(@NotNull PsiExpression wrappedExpression, boolean calculateTypeForComplexReferences) {
ExpectedTypeVisitor(@NotNull PsiExpression wrappedExpression, boolean calculateTypeForComplexReferences, boolean reportCasts) {
this.wrappedExpression = wrappedExpression;
this.calculateTypeForComplexReferences = calculateTypeForComplexReferences;
this.reportCasts = reportCasts;
}
public PsiType getExpectedType() {
@@ -295,6 +301,13 @@ public class ExpectedTypeUtils {
}
}
@Override
public void visitTypeCastExpression(PsiTypeCastExpression expression) {
if (reportCasts) {
expectedType = expression.getType();
}
}
@Override
public void visitWhileStatement(@NotNull PsiWhileStatement whileStatement) {
expectedType = PsiType.BOOLEAN;
@@ -53,4 +53,9 @@ public class AutoBoxing {
void doInstanceof(Object o) {
Boolean b = o instanceof String;
}
void m(boolean b) {
System.out.println((Boolean)b);
final Object o1 = (Object) b;
}
}
@@ -93,4 +93,18 @@
<description>Auto-boxing &lt;code&gt;o instanceof String&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>AutoBoxing.java</file>
<line>58</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Auto-boxing</problem_class>
<description>Auto-boxing &lt;code&gt;b&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>AutoBoxing.java</file>
<line>59</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Auto-boxing</problem_class>
<description>Auto-boxing &lt;code&gt;b&lt;/code&gt; #loc</description>
</problem>
</problems>
@@ -53,4 +53,9 @@ public class AutoUnboxing {
default:
}
}
void m(Object o, Boolean b) {
if ((boolean) o) {}
if ((boolean) b) {}
}
}
@@ -85,4 +85,17 @@
<description>Auto-unboxing &lt;code&gt;i&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>AutoUnboxing.java</file>
<line>58</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Auto-unboxing</problem_class>
<description>Auto-unboxing &lt;code&gt;o&lt;/code&gt; #loc</description>
</problem>
<problem>
<file>AutoUnboxing.java</file>
<line>59</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Auto-unboxing</problem_class>
<description>Auto-unboxing &lt;code&gt;b&lt;/code&gt; #loc</description>
</problem>
</problems>