diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 6880a6645710..d4153a024bd4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -1165,8 +1165,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } private void generateOther(PsiPolyadicExpression expression, IElementType op, PsiExpression[] operands, PsiType type) { - op = substituteBinaryOperation(op, type); - PsiExpression lExpr = operands[0]; lExpr.accept(this); PsiType lType = lExpr.getType(); @@ -1183,14 +1181,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } } - @Nullable - private static IElementType substituteBinaryOperation(IElementType op, PsiType type) { - if (JavaTokenType.PLUS == op && (type == null || !type.equalsToText(JAVA_LANG_STRING))) { - return null; - } - return op; - } - private void acceptBinaryRightOperand(@Nullable IElementType op, PsiType type, PsiExpression lExpr, PsiType lType, PsiExpression rExpr, PsiType rType) { 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 126c48ff5fc5..33cd6afa2d25 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 @@ -20,6 +20,7 @@ import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; +import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -383,7 +384,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { final IElementType opSign = instruction.getOperationSign(); if (opSign != null) { - DfaInstructionState[] states = handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign); + DfaInstructionState[] states = handleConstantBinOp(instruction, runner, memState, dfaRight, dfaLeft, opSign); if (states == null) { states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft); } @@ -466,11 +467,11 @@ public class StandardInstructionVisitor extends InstructionVisitor { } @Nullable - private static DfaInstructionState[] handleConstantComparison(BinopInstruction instruction, - DataFlowRunner runner, - DfaMemoryState memState, - DfaValue dfaRight, - DfaValue dfaLeft, IElementType opSign) { + private static DfaInstructionState[] handleConstantBinOp(BinopInstruction instruction, + DataFlowRunner runner, + DfaMemoryState memState, + DfaValue dfaRight, + DfaValue dfaLeft, IElementType opSign) { if (dfaRight instanceof DfaConstValue && dfaLeft instanceof DfaVariableValue) { Object value = ((DfaConstValue)dfaRight).getValue(); if (value instanceof Number) { @@ -482,10 +483,19 @@ public class StandardInstructionVisitor extends InstructionVisitor { } } if (dfaRight instanceof DfaVariableValue && dfaLeft instanceof DfaConstValue) { - return handleConstantComparison(instruction, runner, memState, dfaLeft, dfaRight, DfaRelationValue.getSymmetricOperation(opSign)); + return handleConstantBinOp(instruction, runner, memState, dfaLeft, dfaRight, DfaRelationValue.getSymmetricOperation(opSign)); } if (EQEQ != opSign && NE != opSign) { + Object value1 = getConstantValue(memState, dfaLeft); + Object value2 = getConstantValue(memState, dfaRight); + if (instruction.getPsiAnchor() instanceof PsiExpression && value1 != null && value2 != null) { + Object result = JavaConstantExpressionEvaluator.computeBinaryOperationResult(value1, value2, opSign, false); + if (result != null) { + memState.push(runner.getFactory().getConstFactory().createFromValue(result, ((PsiExpression)instruction.getPsiAnchor()).getType(), null)); + return nextInstruction(instruction, runner, memState); + } + } return null; } @@ -502,6 +512,12 @@ public class StandardInstructionVisitor extends InstructionVisitor { return null; } + @Nullable + private static Object getConstantValue(DfaMemoryState memState, DfaValue val) { + if (val instanceof DfaVariableValue) val = memState.getConstantValue((DfaVariableValue)val); + return val instanceof DfaConstValue ? ((DfaConstValue)val).getValue() : null; + } + @Nullable private static DfaInstructionState[] checkComparingWithConstant(BinopInstruction instruction, DataFlowRunner runner, diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java index 6e64a6b86416..db53ab844d36 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java @@ -38,7 +38,7 @@ import org.jetbrains.annotations.Nullable; import static com.intellij.psi.JavaTokenType.*; public class BinopInstruction extends BranchingInstruction { - private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS); + private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, ASTERISK, DIV, PERC); private final IElementType myOperationSign; private final Project myProject; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java b/java/java-psi-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java index 44eab8b6f351..3ec6eb4d17f4 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/ConstantExpressionVisitor.java @@ -132,7 +132,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan } } - private Object compute(Object lOperandValue, Object rOperandValue, IElementType tokenType, PsiElement expression) { + Object compute(Object lOperandValue, Object rOperandValue, IElementType tokenType, PsiElement expression) { Object value = null; if (tokenType == JavaTokenType.PLUS) { if (lOperandValue instanceof String || rOperandValue instanceof String) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/JavaConstantExpressionEvaluator.java b/java/java-psi-impl/src/com/intellij/psi/impl/JavaConstantExpressionEvaluator.java index 73b72a0943b0..357c15ba05c1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/JavaConstantExpressionEvaluator.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/JavaConstantExpressionEvaluator.java @@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Factory; import com.intellij.openapi.util.Key; import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -29,6 +30,7 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentMap; @@ -142,4 +144,11 @@ public class JavaConstantExpressionEvaluator extends JavaRecursiveElementWalking public static Object computeConstantExpression(@Nullable PsiExpression expression, boolean throwExceptionOnOverflow) { return computeConstantExpression(expression, null, throwExceptionOnOverflow); } + + @Nullable + public static Object computeBinaryOperationResult(@NotNull Object lOperandValue, + @NotNull Object rOperandValue, + @NotNull IElementType tokenType, boolean throwExceptionOnOverflow) { + return new ConstantExpressionVisitor(Collections.emptySet(), throwExceptionOnOverflow, null).compute(lOperandValue, rOperandValue, tokenType, null); + } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ConstantArithmetic.java b/java/java-tests/testData/inspection/dataFlow/fixture/ConstantArithmetic.java new file mode 100644 index 000000000000..30531e537242 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ConstantArithmetic.java @@ -0,0 +1,17 @@ +class Test { + { + int a = 0; + int b = a; + System.out.println(a == b); + System.out.println(a - b); + } + + { + int a = 1; + int b = -3; + System.out.println(a == b); + if (a + b + 2 == 0) { + System.out.println("a"); + } + } +} \ 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 d79d3fca6a90..90c2c20ac21f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -423,4 +423,11 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testConstantConditionsWithAssignmentsInside() { doTest(); } public void testIfConditionsWithAssignmentInside() { doTest(); } + + public void testConstantArithmetic() { + final DataFlowInspection inspection = new DataFlowInspection(); + inspection.REPORT_CONSTANT_REFERENCE_VALUES = true; + myFixture.enableInspections(inspection); + myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); + } }