mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
IntegerMultiplicationImplicitCastToLong: added quick-fix (IDEA-206024)
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
void test(int a, int b) {
|
||||
long c = a * 2L;
|
||||
long d = 2L * a;
|
||||
long d1 = -2L * a;
|
||||
long e = (long) a * b;
|
||||
long f = (long) a * b * 2; // should be converted to (long) a * b * 2 or 2L * a * b (but not a*b*2L: in this case a*b would still be integer)
|
||||
long g = (long) a << 2;
|
||||
long h = 2L << a;
|
||||
long i = (2L) * a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
|
||||
void test (int i1, int i2, int i3) {
|
||||
long a = (long) (((long) i1 * i2) * i3);
|
||||
long b = (int) ((i1 * i2) * i3); // shouldn't convert because result is still casted to int
|
||||
long c = ((long) (int) i1 * i2 * i3);
|
||||
long d = ((long) i1 * i2 * i3); // fix is not needed at all
|
||||
long e = (long) ((long) i1 * i2);
|
||||
long f = (i2) * (long) i1; // fix is not needed at all
|
||||
long g = (long) i1 * ((long) i2 * i3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
void test(int a, int b) {
|
||||
long c = a * ((long) a * b);
|
||||
long d = (a * (b * (a * (b * 2L)));
|
||||
long e = -(-a * -((long) -a * -b));
|
||||
long f = a * ((a == 2) ? (long) b * a : a);
|
||||
long g = a + (a + ((long) (a + b) * (a + b)));
|
||||
long h = (long) a << (a * b);
|
||||
long i = ((long) a * b) << (b * a);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
void test(int a, int b) {
|
||||
long c = <caret>a * 2;
|
||||
long d = 2 * a;
|
||||
long d1 = -2 * a;
|
||||
long e = a * b;
|
||||
long f = a * b * 2; // should be converted to (long) a * b * 2 or 2L * a * b (but not a*b*2L: in this case a*b would still be integer)
|
||||
long g = a << 2;
|
||||
long h = 2 << a;
|
||||
long i = (2) * a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
|
||||
void test (int i1, int i2, int i3) {
|
||||
long a = (long) ((<caret>i1 * i2) * i3);
|
||||
long b = (int) ((i1 * i2) * i3); // shouldn't convert because result is still casted to int
|
||||
long c = ((int) i1 * i2 * i3);
|
||||
long d = ((long) i1 * i2 * i3); // fix is not needed at all
|
||||
long e = (long) (i1 * i2);
|
||||
long f = (i2) * (long) i1; // fix is not needed at all
|
||||
long g = (long) i1 * (i2 * i3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Fix all 'Integer multiplication or shift implicitly cast to long' problems in file" "true"
|
||||
class Test {
|
||||
void test(int a, int b) {
|
||||
long c = a * (<caret>a * b);
|
||||
long d = (a * (b * (a * (b * 2)));
|
||||
long e = -(-a * -(-a * -b));
|
||||
long f = a * ((a == 2) ? b * a : a);
|
||||
long g = a + (a + ((a + b) * (a + b)));
|
||||
long h = a << (a * b);
|
||||
long i = (a * b) << (b * a);
|
||||
}
|
||||
}
|
||||
@@ -1420,6 +1420,7 @@ notify.without.corresponding.wait.display.name='notify()' without corresponding
|
||||
notify.without.corresponding.wait.problem.descriptor=Call to <code>#ref</code> without corresponding <code>wait()</code> #loc
|
||||
integer.multiplication.implicit.cast.to.long.display.name=Integer multiplication or shift implicitly cast to long
|
||||
integer.multiplication.implicit.cast.to.long.problem.descriptor=#ref: integer multiplication implicitly cast to long #loc
|
||||
integer.multiplication.implicit.cast.to.long.quickfix=Cast to long
|
||||
integer.shift.implicit.cast.to.long.problem.descriptor=#ref: integer shift implicitly cast to long #loc
|
||||
integer.multiplication.implicit.cast.to.long.option=<html>Ignore expressions where it's proven statically that overflow is impossible</html>
|
||||
wait.or.await.without.timeout.display.name='wait()' or 'await()' without timeout
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.siyeh.ig.numeric;
|
||||
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.CommonDataflow;
|
||||
import com.intellij.codeInspection.dataFlow.DfaFactType;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.ConstantEvaluationOverflowException;
|
||||
@@ -27,12 +29,17 @@ import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.InspectionGadgetsFix;
|
||||
import com.siyeh.ig.PsiReplacementUtil;
|
||||
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -64,6 +71,12 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe
|
||||
"integer.multiplication.implicit.cast.to.long.display.name");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionGadgetsFix buildFix(Object... infos) {
|
||||
return new IntegerMultiplicationImplicitCastToLongInspectionFix();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected String buildErrorString(Object... infos) {
|
||||
@@ -88,6 +101,109 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe
|
||||
return new IntegerMultiplicationImplicitlyCastToLongVisitor();
|
||||
}
|
||||
|
||||
private static boolean isNonLongInteger(PsiType type) {
|
||||
if (type == null) return false;
|
||||
final String text = type.getCanonicalText();
|
||||
return s_typesToCheck.contains(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether one of operands of polyadic expression itself is polyadic expression with multiplication operator.
|
||||
* For shift operations only first operand is considered.
|
||||
*/
|
||||
private static boolean hasInnerMultiplication(@NotNull PsiPolyadicExpression expression) {
|
||||
final IElementType tokenType = expression.getOperationTokenType();
|
||||
if (isShiftToken(tokenType)) {
|
||||
return hasMultiplication(expression.getOperands()[0]);
|
||||
}
|
||||
|
||||
return Arrays.stream(expression.getOperands()).anyMatch(operand -> hasMultiplication(operand));
|
||||
}
|
||||
|
||||
private static boolean hasMultiplication(PsiExpression expression) {
|
||||
expression = PsiUtil.deparenthesizeExpression(expression);
|
||||
|
||||
if (expression instanceof PsiPrefixExpression) {
|
||||
return hasMultiplication(((PsiPrefixExpression)expression).getOperand());
|
||||
}
|
||||
|
||||
if (expression instanceof PsiPolyadicExpression) {
|
||||
final PsiPolyadicExpression polyExpr = (PsiPolyadicExpression)expression;
|
||||
final IElementType tokenType = polyExpr.getOperationTokenType();
|
||||
|
||||
if (tokenType == JavaTokenType.ASTERISK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hasInnerMultiplication(polyExpr);
|
||||
}
|
||||
|
||||
if (expression instanceof PsiConditionalExpression) {
|
||||
final PsiConditionalExpression ternary = (PsiConditionalExpression)expression;
|
||||
return hasMultiplication(ternary.getThenExpression()) || hasMultiplication(ternary.getElseExpression());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isShiftToken(IElementType tokenType) {
|
||||
return tokenType.equals(JavaTokenType.LTLT) ||
|
||||
tokenType.equals(JavaTokenType.GTGT) ||
|
||||
tokenType.equals(JavaTokenType.GTGTGT);
|
||||
}
|
||||
|
||||
private static class IntegerMultiplicationImplicitCastToLongInspectionFix extends InspectionGadgetsFix {
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("integer.multiplication.implicit.cast.to.long.quickfix");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFix(Project project, ProblemDescriptor descriptor) {
|
||||
final PsiPolyadicExpression expression = (PsiPolyadicExpression)descriptor.getPsiElement();
|
||||
|
||||
final PsiExpression[] operands = expression.getOperands();
|
||||
if (operands.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiExpression exprToCast;
|
||||
if (operands.length > 2 || expression.getOperationTokenType() == JavaTokenType.LTLT) {
|
||||
exprToCast = operands[0];
|
||||
}
|
||||
else {
|
||||
exprToCast = Arrays.stream(operands)
|
||||
.map(operand -> PsiUtil.deparenthesizeExpression(operand))
|
||||
.filter(operand -> operand instanceof PsiLiteralExpression ||
|
||||
operand instanceof PsiPrefixExpression && ((PsiPrefixExpression)operand).getOperand() instanceof PsiLiteral)
|
||||
.findFirst()
|
||||
.orElse(operands[0]);
|
||||
}
|
||||
|
||||
addCast(exprToCast);
|
||||
}
|
||||
|
||||
private static void addCast(@NotNull PsiExpression expression) {
|
||||
if (expression instanceof PsiPrefixExpression) {
|
||||
final PsiExpression operand = ((PsiPrefixExpression)expression).getOperand();
|
||||
if (operand instanceof PsiLiteralExpression) expression = operand;
|
||||
}
|
||||
|
||||
final String replacementText;
|
||||
if (expression instanceof PsiLiteralExpression) {
|
||||
replacementText = expression.getText() + "L";
|
||||
}
|
||||
else {
|
||||
replacementText = "(long)" + expression.getText();
|
||||
}
|
||||
|
||||
PsiReplacementUtil.replaceExpression(expression, replacementText);
|
||||
}
|
||||
}
|
||||
|
||||
private class IntegerMultiplicationImplicitlyCastToLongVisitor
|
||||
extends BaseInspectionVisitor {
|
||||
|
||||
@@ -102,20 +218,24 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe
|
||||
if (!isNonLongInteger(type)) {
|
||||
return;
|
||||
}
|
||||
if (hasInnerMultiplication(expression)) {
|
||||
return;
|
||||
}
|
||||
PsiExpression[] operands = expression.getOperands();
|
||||
if (operands.length < 2 || expression.getLastChild() instanceof PsiErrorElement) {
|
||||
return;
|
||||
}
|
||||
final PsiExpression context = getContainingExpression(expression);
|
||||
PsiExpression context = getContainingExpression(expression);
|
||||
if (context == null) return;
|
||||
if (!PsiType.LONG.equals(context.getType()) &&
|
||||
!PsiType.LONG.equals(ExpectedTypeUtils.findExpectedType(context, true))) {
|
||||
return;
|
||||
}
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(context.getParent());
|
||||
if (parent instanceof PsiTypeCastExpression) {
|
||||
PsiType castType = ((PsiTypeCastExpression)parent).getType();
|
||||
if (isNonLongInteger(castType)) return;
|
||||
if (PsiType.LONG.equals(castType)) context = (PsiExpression)parent;
|
||||
}
|
||||
if (!PsiType.LONG.equals(context.getType()) &&
|
||||
!PsiType.LONG.equals(ExpectedTypeUtils.findExpectedType(context, true))) {
|
||||
return;
|
||||
}
|
||||
if (ignoreNonOverflowingCompileTimeConstants) {
|
||||
try {
|
||||
@@ -177,8 +297,9 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe
|
||||
PsiExpression expression) {
|
||||
final PsiElement parent = expression.getParent();
|
||||
if (parent instanceof PsiPolyadicExpression && TypeConversionUtil.isNumericType(((PsiPolyadicExpression)parent).getType())) {
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
if (!tokenType.equals(JavaTokenType.LTLT) && !tokenType.equals(JavaTokenType.GTGT) && !tokenType.equals(JavaTokenType.GTGTGT)) {
|
||||
final PsiPolyadicExpression polyParent = (PsiPolyadicExpression)parent;
|
||||
final IElementType tokenType = polyParent.getOperationTokenType();
|
||||
if (!isShiftToken(tokenType) || expression == polyParent.getOperands()[0]) {
|
||||
return getContainingExpression((PsiExpression)parent);
|
||||
}
|
||||
}
|
||||
@@ -189,11 +310,5 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
private boolean isNonLongInteger(PsiType type) {
|
||||
if (type == null) return false;
|
||||
final String text = type.getCanonicalText();
|
||||
return s_typesToCheck.contains(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,19 @@ public class IntegerMultiplicationImplicitCastToLong {
|
||||
// Shift operations are not subjected to binary promotion (JLS 15.19), operands are promoted separately using unary promotion rules
|
||||
long l = 1L << (step * 8);
|
||||
}
|
||||
|
||||
void leftArgOfShift(int i) {
|
||||
long l = (<warning descr="i * i: integer multiplication implicitly cast to long">i * i</warning>) << 8;
|
||||
}
|
||||
|
||||
void testConcat(int step) {
|
||||
String res = "Result: "+(1L + <warning descr="1000*step: integer multiplication implicitly cast to long">1000*step</warning>);
|
||||
}
|
||||
|
||||
void reportOnlyInnerMultInMultOrShift(int i) {
|
||||
long l = (i * (<warning descr="i * i: integer multiplication implicitly cast to long">i * i</warning>));
|
||||
long l1 = (<warning descr="i * i: integer multiplication implicitly cast to long">i * i</warning>) << 1;
|
||||
}
|
||||
|
||||
public void foo() {
|
||||
int x = 65336;
|
||||
@@ -58,4 +67,8 @@ public class IntegerMultiplicationImplicitCastToLong {
|
||||
int explicitCast(int a, long b) {
|
||||
return (int)(a * 300 + b);
|
||||
}
|
||||
|
||||
long longCastAfterOverflow(int a, int b, int c) {
|
||||
return (long) (<warning descr="a * b * c: integer multiplication implicitly cast to long">a * b * c</warning>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.numeric;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IntegerMultiplicationImplicitCastToLongInspectionFixTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new IntegerMultiplicationImplicitCastToLongInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/implicitCastToLong";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user