diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index be70ba0e4328..784db54634a5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -310,7 +310,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { final IElementType opSign = instruction.getOperationSign(); if (opSign != null) { - DfaInstructionState[] states = handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft); + DfaInstructionState[] states = handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign); if (states == null) { states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft); } @@ -422,8 +422,21 @@ public class StandardInstructionVisitor extends InstructionVisitor { DataFlowRunner runner, DfaMemoryState memState, DfaValue dfaRight, - DfaValue dfaLeft) { - final IElementType opSign = instruction.getOperationSign(); + DfaValue dfaLeft, IElementType opSign) { + if (dfaRight instanceof DfaConstValue && dfaLeft instanceof DfaVariableValue) { + PsiType varType = ((DfaVariableValue)dfaLeft).getVariableType(); + Object value = ((DfaConstValue)dfaRight).getValue(); + if (varType instanceof PsiPrimitiveType && value instanceof Number) { + DfaInstructionState[] result = checkTypeRanges(instruction, runner, memState, opSign, varType, ((Number)value).longValue()); + if (result != null) { + return result; + } + } + } + if (dfaRight instanceof DfaVariableValue && dfaLeft instanceof DfaConstValue) { + return handleConstantComparison(instruction, runner, memState, dfaLeft, dfaRight, DfaRelationValue.getSymmetricOperation(opSign)); + } + if (EQEQ != opSign && NE != opSign || !(dfaLeft instanceof DfaConstValue) || !(dfaRight instanceof DfaConstValue)) { return null; @@ -431,13 +444,49 @@ public class StandardInstructionVisitor extends InstructionVisitor { boolean negated = (NE == opSign) ^ (DfaMemoryStateImpl.isNaN(dfaLeft) || DfaMemoryStateImpl.isNaN(dfaRight)); if (dfaLeft == dfaRight ^ negated) { - memState.push(runner.getFactory().getConstFactory().getTrue()); - instruction.setTrueReachable(); + return alwaysTrue(instruction, runner, memState); } - else { - memState.push(runner.getFactory().getConstFactory().getFalse()); - instruction.setFalseReachable(); + return alwaysFalse(instruction, runner, memState); + } + + private static DfaInstructionState[] checkTypeRanges(BinopInstruction instruction, + DataFlowRunner runner, + DfaMemoryState memState, + IElementType opSign, PsiType varType, long constantValue) { + long minValue = varType == PsiType.BYTE ? Byte.MIN_VALUE : + varType == PsiType.SHORT ? Short.MIN_VALUE : + varType == PsiType.INT ? Integer.MIN_VALUE : + varType == PsiType.CHAR ? Character.MIN_VALUE : + Long.MIN_VALUE; + long maxValue = varType == PsiType.BYTE ? Byte.MAX_VALUE : + varType == PsiType.SHORT ? Short.MAX_VALUE : + varType == PsiType.INT ? Integer.MAX_VALUE : + varType == PsiType.CHAR ? Character.MAX_VALUE : + Long.MAX_VALUE; + + if (constantValue < minValue || constantValue > maxValue) { + if (opSign == EQEQ) return alwaysFalse(instruction, runner, memState); + if (opSign == NE) return alwaysTrue(instruction, runner, memState); } + + if (opSign == LT && constantValue <= minValue) return alwaysFalse(instruction, runner, memState); + if ((opSign == LT || opSign == LE) && constantValue > maxValue) return alwaysTrue(instruction, runner, memState); + + if (opSign == GT && constantValue >= maxValue) return alwaysFalse(instruction, runner, memState); + if ((opSign == GT || opSign == GE) && constantValue < minValue) return alwaysTrue(instruction, runner, memState); + + return null; + } + + private static DfaInstructionState[] alwaysFalse(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { + memState.push(runner.getFactory().getConstFactory().getFalse()); + instruction.setFalseReachable(); + return nextInstruction(instruction, runner, memState); + } + + private static DfaInstructionState[] alwaysTrue(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { + memState.push(runner.getFactory().getConstFactory().getTrue()); + instruction.setTrueReachable(); return nextInstruction(instruction, runner, memState); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java index c3593f6eca21..8343fc9e6a2c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java @@ -114,13 +114,14 @@ public class DfaRelationValue extends DfaValue { return result; } - private static IElementType getSymmetricOperation(IElementType sign) { - if (LT == sign) return GT; - if (GE == sign) return LE; - if (GT == sign) return LT; - if (LE == sign) return GE; - return sign; - } + } + + public static IElementType getSymmetricOperation(IElementType sign) { + if (LT == sign) return GT; + if (GE == sign) return LE; + if (GT == sign) return LT; + if (LE == sign) return GE; + return sign; } private DfaRelationValue(DfaValueFactory factory) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/InherentNumberRanges.java b/java/java-tests/testData/inspection/dataFlow/fixture/InherentNumberRanges.java new file mode 100644 index 000000000000..09ec04884108 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/InherentNumberRanges.java @@ -0,0 +1,19 @@ +class Foo { + + private void foo(byte myByte) { + if (myByte == 0xFF) { + System.out.println("wrong"); + } + if (myByte != 0xFFF) { + System.out.println("wrong"); + } + if (myByte == (byte) 0xFF) { + System.out.println("right"); + } + } + + void foo2(int i) { + boolean a = i < 0x80000000L; + boolean f = i > 0x7fffffff; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 58dcf3ecc0f1..8de5e670edc2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -179,6 +179,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { } public void testConstantDoubleComparisons() { doTest(); } + public void testInherentNumberRanges() { doTest(); } public void testMutableNullableFieldsTreatment() { doTest(); } public void testMutableVolatileNullableFieldsTreatment() { doTest(); } diff --git a/platform/util/src/com/intellij/util/containers/VariableWidthIntArray.java b/platform/util/src/com/intellij/util/containers/VariableWidthIntArray.java deleted file mode 100644 index d73a732ba447..000000000000 --- a/platform/util/src/com/intellij/util/containers/VariableWidthIntArray.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * 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.intellij.util.containers; - -import com.intellij.openapi.diagnostic.Logger; - -/** - * @author Dmitry.Shtukenberg - */ -public final class VariableWidthIntArray implements Cloneable { - private static final Logger LOG = Logger.getInstance("#com.intellij.util.containers.VariableWidthIntArray"); - - private int[] intArray = null; - private short[] shortArray = null; - private byte[] byteArray = null; - - private final int minValue; - private final int maxValue; - - static final int INT = 1; - static final int SHORT = 2; - static final int BYTE = 3; - private final int arrayType; - - public VariableWidthIntArray( int minValue, int maxValue, int initialCapacity ) { - this.minValue = minValue; - this.maxValue = maxValue; - - if( minValue < Short.MIN_VALUE || maxValue > Short.MAX_VALUE ) { - intArray = new int[ bestSize(initialCapacity) ]; - arrayType = INT; - } - else if( minValue < Byte.MIN_VALUE || maxValue > Byte.MAX_VALUE ) { - shortArray = new short[ bestSize(initialCapacity) ]; - arrayType = SHORT; - } - else { - byteArray = new byte[ bestSize(initialCapacity) ]; - arrayType = BYTE; - } - } - - static int bestSize( int maxSize ) { - int newSize = (int)(((double)maxSize) * 5 / 4); - return newSize <= maxSize ? maxSize + 1 : newSize; - } - - public void ensureArraySize( int index ) { - if( intArray != null && intArray.length <= index ) { - int[] tmp = new int[ bestSize(index) ]; - System.arraycopy( intArray, 0, tmp, 0, intArray.length ); - intArray = tmp; - } - else if( shortArray != null && shortArray.length <= index ) { - short[] tmp = new short[ bestSize(index) ]; - System.arraycopy( shortArray, 0, tmp, 0, shortArray.length ); - shortArray = tmp; - } - else if( byteArray.length <= index ) { - byte[] tmp = new byte[ bestSize(index) ]; - System.arraycopy( byteArray, 0, tmp, 0, byteArray.length ); - byteArray = tmp; - } - } - - public int get( int index ) { - switch( arrayType ) { - case INT: return intArray[index]; - case SHORT: return shortArray[index]; - case BYTE: return byteArray[index]; - } - LOG.error("No array allocated"); - return 0; - } - - public void put( int index, int value ) { - if( value < minValue || value > maxValue ) { - LOG.error("Value out of domain"); - } - - switch( arrayType ) { - case INT: intArray[index] = value; return; - case SHORT: shortArray[index] = (short)value; return; - case BYTE: byteArray[index] = (byte)value; return; - } - - LOG.error("No array allocated"); - } - - @Override - public Object clone() throws CloneNotSupportedException { - VariableWidthIntArray arr = (VariableWidthIntArray)super.clone(); - if( intArray != null ) { arr.intArray = intArray.clone(); } - if( shortArray != null ) { arr.shortArray = shortArray.clone(); } - if( byteArray != null ) { arr.byteArray = byteArray.clone(); } - return arr; - } - - public void arraycopy(int[] src, int from, int to, int count) { - for( int i = 0; i < count; i++ ) - put( i + to, src[i + from] ); - } - - public void move(int from, int to, int count) { - switch( arrayType ) { - case INT: System.arraycopy( intArray, from, intArray, to, count ); break; - case SHORT: System.arraycopy( shortArray, from, shortArray, to, count ); break; - case BYTE: System.arraycopy( byteArray, from, byteArray, to, count ); break; - default: - LOG.error("Invalid array type"); - } - } - - public boolean arrayequal( VariableWidthIntArray src, int srcfrom, int from, int count ) { - if( src.arrayType != arrayType ) return false; - for( int i = 0; i < count; i++ ) - if( get(from+i) != src.get(srcfrom+i)) return false; - return true; - } -}