IDEA-21350 (Add "Unnecessary explicit numeric cast" inspection)

This commit is contained in:
Bas Leijdekkers
2011-05-24 15:36:56 +02:00
parent b09a46997d
commit 2ced0c5790
5 changed files with 340 additions and 0 deletions
@@ -1101,6 +1101,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent,
m_inspectionClasses.add(OverlyComplexArithmeticExpressionInspection.class);
m_inspectionClasses.add(PointlessArithmeticExpressionInspection.class);
m_inspectionClasses.add(UnaryPlusInspection.class);
m_inspectionClasses.add(UnnecessaryExplicitNumericCastInspection.class);
m_inspectionClasses.add(UnnecessaryUnaryMinusInspection.class);
m_inspectionClasses.add(UnpredictableBigDecimalConstructorCallInspection.class);
}
@@ -0,0 +1,252 @@
/*
* Copyright 2011 Bas Leijdekkers
*
* 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.numeric;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.ClassUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class UnnecessaryExplicitNumericCastInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return "Unnecessary explicit numeric cast";
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
final PsiExpression expression = (PsiExpression) infos[0];
return " '" + expression.getText() + "' unnecessarily cast to <code>#ref</code>";
}
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return new UnnecessaryExplicitNumericCastFix();
}
private static class UnnecessaryExplicitNumericCastFix
extends InspectionGadgetsFix {
@NotNull
@Override
public String getName() {
return "Remove cast";
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor)
throws IncorrectOperationException {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiTypeCastExpression)) {
return;
}
final PsiTypeCastExpression typeCastExpression =
(PsiTypeCastExpression) parent;
if (isPrimitiveNumericCastNecessary(typeCastExpression)) {
return;
}
final PsiExpression operand = typeCastExpression.getOperand();
if (operand == null) {
typeCastExpression.delete();
} else {
typeCastExpression.replace(operand);
}
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new UnnecessaryExplicitNumericCastVisitor();
}
private static class UnnecessaryExplicitNumericCastVisitor
extends BaseInspectionVisitor {
@Override
public void visitTypeCastExpression(PsiTypeCastExpression expression) {
super.visitTypeCastExpression(expression);
final PsiType castType = expression.getType();
if (!ClassUtils.isPrimitiveNumericType(castType)) {
return;
}
final PsiExpression operand = expression.getOperand();
if (operand == null) {
return;
}
final PsiType operandType = operand.getType();
if (operandType == null || operandType.equals(castType)) {
return;
}
if (isPrimitiveNumericCastNecessary(expression)) {
return;
}
final PsiTypeElement typeElement = expression.getCastType();
if (typeElement != null) {
registerError(typeElement, operand);}
}
}
static boolean isPrimitiveNumericCastNecessary(
PsiTypeCastExpression expression) {
final PsiType type = expression.getType();
if (type == null) {
return true;
}
final PsiExpression operand = expression.getOperand();
if (operand == null) {
return true;
}
final PsiType operandType = operand.getType();
final PsiElement parent = expression.getParent();
if (parent instanceof PsiBinaryExpression) {
if (PsiType.INT.equals(type)) {
return PsiType.LONG.equals(operandType) ||
PsiType.FLOAT.equals(operandType) ||
PsiType.DOUBLE.equals(operandType);
}
if (PsiType.LONG.equals(type) || PsiType.FLOAT.equals(type) ||
PsiType.DOUBLE.equals(type)) {
final PsiBinaryExpression binaryExpression =
(PsiBinaryExpression) parent;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (rhs == null) {
return true;
}
if (expression == lhs) {
final PsiType rhsType = rhs.getType();
if (type.equals(rhsType)) {
return false;
}
} else if (expression == rhs) {
final PsiType lhsType = lhs.getType();
if (type.equals(lhsType)) {
return false;
}
} else {
assert false;
}
}
} else if (parent instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression =
(PsiAssignmentExpression) parent;
final PsiType lhsType = assignmentExpression.getType();
if (!type.equals(lhsType)) {
return true;
}
return !isHandledByAssignmentConversion(lhsType, operand);
} else if (parent instanceof PsiVariable) {
final PsiVariable variable = (PsiVariable) parent;
final PsiType lhsType = variable.getType();
if (!type.equals(lhsType)) {
return true;
}
return !isHandledByAssignmentConversion(lhsType, operand);
}
return true;
}
static boolean isHandledByAssignmentConversion(PsiType lhsType,
PsiExpression operand) {
// JLS 5.2 Assignment Conversion
final PsiType operandType = operand.getType();
if (PsiType.DOUBLE.equals(lhsType)) {
if (PsiType.FLOAT.equals(operandType) ||
PsiType.LONG.equals(operandType) ||
PsiType.INT.equals(operandType) ||
PsiType.CHAR.equals(operandType) ||
PsiType.SHORT.equals(operandType) ||
PsiType.BYTE.equals(operandType)) {
// widening
return true;
}
} else if (PsiType.FLOAT.equals(lhsType)) {
if (PsiType.LONG.equals(operandType) ||
PsiType.INT.equals(operandType) ||
PsiType.CHAR.equals(operandType) ||
PsiType.SHORT.equals(operandType) ||
PsiType.BYTE.equals(operandType)) {
// widening
return true;
}
} else if (PsiType.LONG.equals(lhsType)) {
if (PsiType.INT.equals(operandType) ||
PsiType.CHAR.equals(operandType) ||
PsiType.SHORT.equals(operandType) ||
PsiType.BYTE.equals(operandType)) {
// widening
return true;
}
} else if (PsiType.INT.equals(lhsType)) {
if (PsiType.CHAR.equals(operandType) ||
PsiType.SHORT.equals(operandType) ||
PsiType.BYTE.equals(operandType)) {
// widening
return true;
}
} else if (PsiType.SHORT.equals(lhsType)) {
if (PsiType.INT.equals(operandType)) {
final Object constant =
ExpressionUtils.computeConstantExpression(operand);
if (!(constant instanceof Integer)) {
return false;
}
final int i = ((Integer) constant).intValue();
if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
// narrowing
return true;
}
}
} else if (PsiType.CHAR.equals(lhsType)) {
if (PsiType.INT.equals(operandType)) {
final Object constant =
ExpressionUtils.computeConstantExpression(operand);
if (!(constant instanceof Integer)) {
return false;
}
final int i = ((Integer) constant).intValue();
if (i >= Character.MIN_VALUE && i <= Character.MAX_VALUE) {
// narrowing
return true;
}
}
} else if (PsiType.BYTE.equals(lhsType)) {
if (PsiType.INT.equals(operandType)) {
final Object constant =
ExpressionUtils.computeConstantExpression(operand);
if (!(constant instanceof Integer)) {
return false;
}
final int i = ((Integer) constant).intValue();
if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
// narrowing
return true;
}
}
}
return false;
}
}
@@ -0,0 +1,25 @@
package com.siyeh.igtest.numeric.unnecessary_explicit_numeric_cast;
public class UnnecessaryExplicitNumericCast {
void a(byte b) {
double d = (double) 1;
d = (double) 1.0f;
d = (double) b;
char c = (char) 1;
b = (int)7;
}
double b(int a, byte b) {
return (double)a * (double) b;
}
public static void main(String[] args) {
int i = 10;
double d = 123.0 / (456.0 * (double) i);
}
}
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>9</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'1' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>10</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'1.0f' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>11</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'b' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>12</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'1' unnecessarily cast to &lt;code&gt;char&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>17</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'a' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>17</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'b' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
<problem>
<file>UnnecessaryExplicitNumericCast.java</file>
<line>23</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Unnecessary explicit numeric cast</problem_class>
<description>'i' unnecessarily cast to &lt;code&gt;double&lt;/code&gt;</description>
</problem>
</problems>
@@ -0,0 +1,11 @@
package com.siyeh.ig.numeric;
import com.siyeh.ig.IGInspectionTestCase;
public class UnnecessaryExplicitNumericCastInspectionTest extends IGInspectionTestCase {
public void test() throws Exception {
doTest("com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast",
new UnnecessaryExplicitNumericCastInspection ());
}
}