diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties index 88a5eeff6c7b..ee55c2d5f6f4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/resources/messages/InspectionGadgetsBundle.properties @@ -838,6 +838,7 @@ confusing.main.method.problem.descriptor=Method named #ref without upper.case.field.name.not.constant.problem.descriptor=Non-constant field #ref with constant-style name #loc dollar.sign.in.name.problem.descriptor=Identifier #ref contains '$' #loc integer.division.in.floating.point.context.problem.descriptor=#ref: integer division in floating-point context #loc +integer.division.in.floating.point.context.quickfix=Cast the dividend to a floating-point type comparison.of.short.and.char.problem.descriptor=Equality comparison #ref of short and char values #loc big.decimal.equals.problem.descriptor=#ref() between BigDecimal values should probably be 'compareTo()' #loc divide.by.zero.problem.descriptor=Division by zero #loc diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/IntegerDivisionInFloatingPointContextInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/IntegerDivisionInFloatingPointContextInspection.java index 991e184dc03b..2d9291d5254a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/IntegerDivisionInFloatingPointContextInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/IntegerDivisionInFloatingPointContextInspection.java @@ -15,15 +15,21 @@ */ package com.siyeh.ig.numeric; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ComparisonUtils; import com.siyeh.ig.psiutils.ExpectedTypeUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Set; @@ -53,6 +59,11 @@ public class IntegerDivisionInFloatingPointContextInspection extends BaseInspect "integer.division.in.floating.point.context.problem.descriptor"); } + @Override + protected @Nullable InspectionGadgetsFix buildFix(Object... infos) { + return new IntegerDivisionInFloatingPointContextFix(); + } + @Override public BaseInspectionVisitor buildVisitor() { return new IntegerDivisionInFloatingPointContextVisitor(); @@ -60,7 +71,7 @@ public class IntegerDivisionInFloatingPointContextInspection extends BaseInspect private static class IntegerDivisionInFloatingPointContextVisitor extends BaseInspectionVisitor { - IntegerDivisionInFloatingPointContextVisitor() {} + IntegerDivisionInFloatingPointContextVisitor() { } @Override public void visitPolyadicExpression(@NotNull PsiPolyadicExpression expression) { @@ -88,22 +99,54 @@ public class IntegerDivisionInFloatingPointContextInspection extends BaseInspect private static boolean isIntegral(PsiType type) { return type != null && s_integralTypes.contains(type.getCanonicalText()); } + } - private static @NotNull PsiExpression getContainingExpression(@NotNull PsiExpression expression) { - final PsiElement parent = expression.getParent(); - if (parent instanceof PsiBinaryExpression) { - final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)parent; - return !ComparisonUtils.isComparisonOperation(binaryExpression.getOperationTokenType()) - ? getContainingExpression(binaryExpression) - : expression; + private static class IntegerDivisionInFloatingPointContextFix extends InspectionGadgetsFix { + @Override + protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + if (!(descriptor.getPsiElement() instanceof PsiBinaryExpression expression)) { + return; } - else if (parent instanceof PsiPolyadicExpression || - parent instanceof PsiParenthesizedExpression || - parent instanceof PsiPrefixExpression || - parent instanceof PsiConditionalExpression) { - return getContainingExpression((PsiExpression)parent); + + final PsiExpression context = getContainingExpression(expression); + final PsiType contextType = ExpectedTypeUtils.findExpectedType(context, true); + String castTo; + if (PsiType.FLOAT.equals(contextType)) { + castTo = "float"; } - return expression; + else if (PsiType.DOUBLE.equals(contextType)) { + castTo = "double"; + } + else { + return; + } + + PsiExpression operand = expression.getLOperand(); + CommentTracker tracker = new CommentTracker(); + String text = tracker.text(operand, ParenthesesUtils.TYPE_CAST_PRECEDENCE); + tracker.replace(operand, "((" + castTo + ")" + text + ")"); + } + + @Override + public @NotNull String getFamilyName() { + return InspectionGadgetsBundle.message("integer.division.in.floating.point.context.quickfix"); } } + + private static @NotNull PsiExpression getContainingExpression(@NotNull PsiExpression expression) { + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)parent; + return !ComparisonUtils.isComparisonOperation(binaryExpression.getOperationTokenType()) + ? getContainingExpression(binaryExpression) + : expression; + } + else if (parent instanceof PsiPolyadicExpression || + parent instanceof PsiParenthesizedExpression || + parent instanceof PsiPrefixExpression || + parent instanceof PsiConditionalExpression) { + return getContainingExpression((PsiExpression)parent); + } + return expression; + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/IntegerDivisionInFloatingPointContext.html b/plugins/InspectionGadgets/src/inspectionDescriptions/IntegerDivisionInFloatingPointContext.html index 6ea4ead7d006..ff8c35982235 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/IntegerDivisionInFloatingPointContext.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/IntegerDivisionInFloatingPointContext.html @@ -5,8 +5,13 @@ Such division is often an error and may have unexpected results due to the truncation that happens in integer division.

Example:


-  float x = 3.0F + 3/5;
+  float x = 3.0F + 3 * 2 / 5;
 
+

After the quick-fix is applied:

+

+  float x = 3.0F + ((float) (3 * 2)) /5;
+
+ \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.after.java new file mode 100644 index 000000000000..c9aea96369aa --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.after.java @@ -0,0 +1,7 @@ +class ExpectedParenthesizedExpr { + + public void test(Integer i) { + double x = 1 + ((double) (i * 2)) / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.java new file mode 100644 index 000000000000..827d831ffa61 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/ExpectedParenthesizedExpr.java @@ -0,0 +1,7 @@ +class ExpectedParenthesizedExpr { + + public void test(Integer i) { + double x = 1 + i * 2 / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.after.java new file mode 100644 index 000000000000..a7f18e20e15e --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.after.java @@ -0,0 +1,7 @@ +class SimpleDouble { + + public void test(Integer i) { + double x = 1 + ((double) i) / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.java new file mode 100644 index 000000000000..5cb09bb619d1 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleDouble.java @@ -0,0 +1,7 @@ +class SimpleDouble { + + public void test(Integer i) { + double x = 1 + i / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.after.java new file mode 100644 index 000000000000..c03b2f8ffb95 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.after.java @@ -0,0 +1,7 @@ +class SimpleFloat { + + public void test(Integer i) { + float x = 1 + ((float) i) / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.java new file mode 100644 index 000000000000..776722530724 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/integer_division_in_floating_point_context/SimpleFloat.java @@ -0,0 +1,7 @@ +class SimpleFloat { + + public void test(Integer i) { + float x = 1 + i / 5; + System.out.println(x); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/IntegerDivisionInFloatingPointContextFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/IntegerDivisionInFloatingPointContextFixTest.java new file mode 100644 index 000000000000..6ec12c0d86db --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/IntegerDivisionInFloatingPointContextFixTest.java @@ -0,0 +1,26 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.siyeh.ig.fixes.numeric; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.numeric.IntegerDivisionInFloatingPointContextInspection; + +public class IntegerDivisionInFloatingPointContextFixTest extends IGQuickFixesTestCase { + + public void testSimpleFloat() { doTest(); } + public void testSimpleDouble() { doTest(); } + public void testExpectedParenthesizedExpr() { doTest(); } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final IntegerDivisionInFloatingPointContextInspection inspection = new IntegerDivisionInFloatingPointContextInspection(); + myFixture.enableInspections(inspection); + myDefaultHint = InspectionGadgetsBundle.message("integer.division.in.floating.point.context.quickfix"); + } + + @Override + protected String getRelativePath() { + return "numeric/integer_division_in_floating_point_context"; + } +}