From 2ced0c5790f10ca55431a310d6aeea96f7420460 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 24 May 2011 15:36:56 +0200 Subject: [PATCH] IDEA-21350 (Add "Unnecessary explicit numeric cast" inspection) --- .../com/siyeh/ig/InspectionGadgetsPlugin.java | 1 + ...ecessaryExplicitNumericCastInspection.java | 252 ++++++++++++++++++ .../UnnecessaryExplicitNumericCast.java | 25 ++ .../expected.xml | 51 ++++ ...saryExplicitNumericCastInspectionTest.java | 11 + 5 files changed, 340 insertions(+) create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspectionTest.java diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java index 158b3b80fbe7..f96fcceb0065 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java @@ -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); } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java new file mode 100644 index 000000000000..acaa5e6d3fdf --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java @@ -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 #ref"; + } + + @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; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java new file mode 100644 index 000000000000..d9894dd1674f --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java @@ -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); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml new file mode 100644 index 000000000000..5d779fbe7db2 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml @@ -0,0 +1,51 @@ + + + + UnnecessaryExplicitNumericCast.java + 9 + Unnecessary explicit numeric cast + '1' unnecessarily cast to <code>double</code> + + + + UnnecessaryExplicitNumericCast.java + 10 + Unnecessary explicit numeric cast + '1.0f' unnecessarily cast to <code>double</code> + + + + UnnecessaryExplicitNumericCast.java + 11 + Unnecessary explicit numeric cast + 'b' unnecessarily cast to <code>double</code> + + + + UnnecessaryExplicitNumericCast.java + 12 + Unnecessary explicit numeric cast + '1' unnecessarily cast to <code>char</code> + + + + UnnecessaryExplicitNumericCast.java + 17 + Unnecessary explicit numeric cast + 'a' unnecessarily cast to <code>double</code> + + + + UnnecessaryExplicitNumericCast.java + 17 + Unnecessary explicit numeric cast + 'b' unnecessarily cast to <code>double</code> + + + + UnnecessaryExplicitNumericCast.java + 23 + Unnecessary explicit numeric cast + 'i' unnecessarily cast to <code>double</code> + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspectionTest.java new file mode 100644 index 000000000000..e0ff6e706e58 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspectionTest.java @@ -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 ()); + } +} \ No newline at end of file