diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java index 7add501df752..954079576e23 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java @@ -699,6 +699,12 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if(diff >= 0 && diff <= MAX_UNROLL_SIZE) { // Unroll small loops Objects.requireNonNull(statement.getUpdate()).accept(this); + // It's possible that a nested loop may cause widening, dismissing our efforts to track the loop variable + // In this case, we should at least ensure that it's not lower than the start. + addInstruction(new PushInstruction(loopVar, null)); + DfType startBound = PsiTypes.intType().equals(counterType) ? DfTypes.intValue(Math.toIntExact(start)) : DfTypes.longValue(start); + addInstruction(new EnsureInstruction(null, RelationType.GE, startBound, null)); + addInstruction(new PopInstruction()); addInstruction(new GotoInstruction(startOffset, false)); return true; } diff --git a/java/java-analysis-impl/src/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspection.java index d81c9f2cf972..5ca685ca5b7c 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspection.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspection.java @@ -17,6 +17,7 @@ package com.siyeh.ig.numeric; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.lang.java.parser.BasicExpressionParser; import com.intellij.modcommand.PsiUpdateModCommandQuickFix; import com.intellij.codeInspection.dataFlow.CommonDataflow; import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet; @@ -28,6 +29,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.ConstantEvaluationOverflowException; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ObjectUtils; @@ -151,7 +153,12 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe } private static boolean isShiftToken(IElementType tokenType) { - return ExpressionParser.SHIFT_OPS.contains(tokenType); + return BasicExpressionParser.SHIFT_OPS.contains(tokenType); + } + + private static boolean isShiftEqToken(@NotNull IElementType tokenType) { + return tokenType.equals(JavaTokenType.LTLTEQ) || tokenType.equals(JavaTokenType.GTGTEQ) || + tokenType.equals(JavaTokenType.GTGTGTEQ); } private static class IntegerMultiplicationImplicitCastToLongInspectionFix extends PsiUpdateModCommandQuickFix { @@ -232,10 +239,15 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe } PsiExpression context = getContainingExpression(expression); PsiElement parent = PsiUtil.skipParenthesizedExprUp(context.getParent()); - if (parent instanceof PsiTypeCastExpression) { - PsiType castType = ((PsiTypeCastExpression)parent).getType(); + if (parent instanceof PsiAssignmentExpression assignment && isShiftEqToken(assignment.getOperationTokenType()) && + PsiTreeUtil.isAncestor(assignment.getRExpression(), context, false)) { + // like 'a <<= b * c'; 'b * c' should not be reported even if 'a' is long + return; + } + if (parent instanceof PsiTypeCastExpression cast) { + PsiType castType = cast.getType(); if (isNonLongInteger(castType)) return; - if (PsiTypes.longType().equals(castType)) context = (PsiExpression)parent; + if (PsiTypes.longType().equals(castType)) context = cast; } if (!PsiTypes.longType().equals(context.getType()) && !PsiTypes.longType().equals(ExpectedTypeUtils.findExpectedType(context, true))) { @@ -256,7 +268,7 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe registerError(expression, tokenType); } - private boolean insideAssertEquals(PsiExpression expression) { + private static boolean insideAssertEquals(PsiExpression expression) { PsiElement parent = ExpressionUtils.getPassThroughParent(expression); if (parent instanceof PsiExpressionList) { PsiMethodCallExpression call = ObjectUtils.tryCast(parent.getParent(), PsiMethodCallExpression.class); @@ -268,7 +280,7 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe return false; } - private boolean cannotOverflow(@NotNull PsiPolyadicExpression expression, PsiExpression[] operands, boolean shift) { + private static boolean cannotOverflow(@NotNull PsiPolyadicExpression expression, PsiExpression[] operands, boolean shift) { CommonDataflow.DataflowResult dfr = CommonDataflow.getDataflowResult(expression); if (dfr != null) { long min = 1, max = 1; @@ -306,17 +318,16 @@ public class IntegerMultiplicationImplicitCastToLongInspection extends BaseInspe return true; } - private boolean intOverflow(long l) { + private static boolean intOverflow(long l) { return (int)l != l && l != Integer.MAX_VALUE + 1L; } - private PsiExpression getContainingExpression( - PsiExpression expression) { + private static PsiExpression getContainingExpression(PsiExpression expression) { final PsiElement parent = expression.getParent(); - if (parent instanceof PsiPolyadicExpression polyParent && TypeConversionUtil.isNumericType(((PsiPolyadicExpression)parent).getType())) { + if (parent instanceof PsiPolyadicExpression polyParent && TypeConversionUtil.isNumericType(polyParent.getType())) { final IElementType tokenType = polyParent.getOperationTokenType(); if (!isShiftToken(tokenType) || expression == polyParent.getOperands()[0]) { - return getContainingExpression((PsiExpression)parent); + return getContainingExpression(polyParent); } } if (parent instanceof PsiParenthesizedExpression || diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/IntegerMultiplicationImplicitCastToLong.java b/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/IntegerMultiplicationImplicitCastToLong.java index b116babdc7a6..c0421b8ec4c9 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/IntegerMultiplicationImplicitCastToLong.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/IntegerMultiplicationImplicitCastToLong.java @@ -4,6 +4,7 @@ public class IntegerMultiplicationImplicitCastToLong { void rightArgOfShift(int step) { // Shift operations are not subjected to binary promotion (JLS 15.19), operands are promoted separately using unary promotion rules long l = 1L << (step * 8); + l <<= step * 8; } void leftArgOfShift(int i) { diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/NestedLoop.java b/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/NestedLoop.java new file mode 100644 index 000000000000..0881fabecf73 --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/numeric/integer_multiplication_implicit_cast_to_long/NestedLoop.java @@ -0,0 +1,11 @@ +public class NestedLoop { + void test() { + long value = 1; + for (int a = 0; a < 2; a++) { + for (int b = 0; b < 2; b++) { + b++; + } + value = 2 * a; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/WidenPlusInLoop.java b/java/java-tests/testData/inspection/dataFlow/fixture/WidenPlusInLoop.java index 6277d720a93a..3f7d0c3f2393 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/WidenPlusInLoop.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/WidenPlusInLoop.java @@ -96,4 +96,15 @@ public class WidenPlusInLoop { // ... } } + + void nestedLoop() { + long value = 1; + for (int a = 0; a < 2; a++) { + for (int b = 0; b < 2; b++) { + b++; + } + if (a >= 0) {} + value <<= 2 * a; + } + } } diff --git a/java/java-tests/testSrc/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspectionTest.java index 8d1d746b247c..59446079560a 100644 --- a/java/java-tests/testSrc/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspectionTest.java +++ b/java/java-tests/testSrc/com/siyeh/ig/numeric/IntegerMultiplicationImplicitCastToLongInspectionTest.java @@ -29,6 +29,10 @@ public class IntegerMultiplicationImplicitCastToLongInspectionTest extends Light doTest(); } + public void testNestedLoop() { + doTest(); + } + @Nullable @Override protected InspectionProfileEntry getInspection() {