From b364143f5a306517d703555e550dc716c09e84d2 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 10 Oct 2018 13:46:10 +0700 Subject: [PATCH] Do not track equality on assignments like Object x = 5 (IDEA-200233) DfaBoxedValue now reports type when possible --- .../dataFlow/ControlFlowAnalyzer.java | 3 ++- .../dataFlow/DfaMemoryStateImpl.java | 17 +++++++++++++---- .../codeInspection/dataFlow/DfaUtil.java | 2 +- .../dataFlow/StandardInstructionVisitor.java | 2 +- .../dataFlow/value/DfaBoxedValue.java | 15 ++++++++++++--- .../dataFlow/value/DfaConstValue.java | 2 +- .../inspection/dataFlow/ancient/CaseAndNpe.java | 2 +- .../LargeIntegersAreNotEqualWhenBoxed.java | 6 ++++++ .../dataFlow/fixture/StringEquality.java | 6 ++++++ 9 files changed, 43 insertions(+), 12 deletions(-) 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 5151ce830203..801d9b5159e0 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 @@ -1448,7 +1448,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } else if (TypeConversionUtil.isPrimitiveAndNotNull(actualType) && TypeConversionUtil.isAssignableFromPrimitiveWrapper(expectedType)) { addConditionalRuntimeThrow(); - addInstruction(new MethodCallInstruction(context, MethodType.BOXING, expectedType)); + PsiType boxedType = ((PsiPrimitiveType)actualType).getBoxedType(context); + addInstruction(new MethodCallInstruction(context, MethodType.BOXING, boxedType)); } else if (actualType != expectedType && TypeConversionUtil.isPrimitiveAndNotNull(actualType) && diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index a65de06343ea..75511798cda4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -235,6 +235,11 @@ public class DfaMemoryStateImpl implements DfaMemoryState { if (value instanceof DfaFactMapValue) { setVariableState(var, state.withFacts(((DfaFactMapValue)value).getFacts())); } + else if (DfaUtil.isComparedByEquals(value.getType()) && !DfaUtil.isComparedByEquals(var.getType())) { + // Like Object x = "foo" or Object x = 5; + TypeConstraint typeConstraint = TypeConstraint.empty().withInstanceofValue(myFactory.createDfaType(value.getType())); + setVariableState(var, new DfaVariableState(getFactMap(value).with(DfaFactType.TYPE_CONSTRAINT, typeConstraint))); + } else { setVariableState(var, isNull(value) ? state.withFact(DfaFactType.NULLABILITY, DfaNullability.NULLABLE) : state); DfaRelationValue dfaEqual = myFactory.getRelationFactory().createRelation(var, RelationType.EQ, value); @@ -761,7 +766,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { DfaValue dfaTrue = myFactory.getConstFactory().getTrue(); if (dfaVar.getSource() == SpecialField.UNBOX) { dfaVar = dfaVar.getQualifier(); - dfaTrue = myFactory.getBoxedFactory().createBoxed(dfaTrue); + dfaTrue = myFactory.getBoxedFactory().createBoxed(dfaTrue, null); } return applyRelationCondition(myFactory.getRelationFactory().createRelation(dfaVar, RelationType.EQ, dfaTrue)); } @@ -1162,7 +1167,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return DfaFactMap.fromDfaValue(value); } - void setVariableState(DfaVariableValue dfaVar, DfaVariableState state) { + void setVariableState(@NotNull DfaVariableValue dfaVar, @NotNull DfaVariableState state) { dfaVar = canonicalize(dfaVar); if (state.equals(getDefaultState(dfaVar))) { myVariableStates.remove(dfaVar); @@ -1188,8 +1193,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState { if (value instanceof DfaVariableValue) { return canonicalize((DfaVariableValue)value); } - if (value instanceof DfaBoxedValue && ((DfaBoxedValue)value).getWrappedValue() instanceof DfaVariableValue) { - return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalize(((DfaBoxedValue)value).getWrappedValue()))); + if (value instanceof DfaBoxedValue) { + DfaBoxedValue boxedValue = (DfaBoxedValue)value; + if (boxedValue.getWrappedValue() instanceof DfaVariableValue) { + DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue()); + return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getType())); + } } if (value instanceof DfaConstValue) { Object constant = ((DfaConstValue)value).getValue(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index aceb50326c74..68aa98a00949 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -347,7 +347,7 @@ public class DfaUtil { if (TypeConversionUtil.isPrimitiveWrapper(type)) { if (value instanceof DfaConstValue || (value instanceof DfaVariableValue && TypeConversionUtil.isPrimitiveAndNotNull(value.getType()))) { - DfaValue boxed = value.getFactory().getBoxedFactory().createBoxed(value); + DfaValue boxed = value.getFactory().getBoxedFactory().createBoxed(value, type); return boxed == null ? DfaUnknownValue.getInstance() : boxed; } } 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 2a69f6f76a2f..b93520b828c6 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 @@ -535,7 +535,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { } if (methodType == MethodCallInstruction.MethodType.BOXING) { - DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue); + DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue, type); return boxed == null ? factory.createTypeValue(type, Nullability.NOT_NULL) : boxed; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java index a8679d6a36b8..ac08aa8ee81d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java @@ -17,6 +17,7 @@ package com.intellij.codeInspection.dataFlow.value; import com.intellij.codeInspection.dataFlow.SpecialField; import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -27,10 +28,12 @@ import java.util.Map; public class DfaBoxedValue extends DfaValue { private final DfaValue myWrappedValue; + private final @Nullable PsiType myType; - private DfaBoxedValue(DfaValue valueToWrap, DfaValueFactory factory) { + private DfaBoxedValue(DfaValue valueToWrap, DfaValueFactory factory, @Nullable PsiType type) { super(factory); myWrappedValue = valueToWrap; + myType = type; } @NonNls @@ -42,6 +45,12 @@ public class DfaBoxedValue extends DfaValue { return myWrappedValue; } + @Nullable + @Override + public PsiType getType() { + return myType; + } + public static class Factory { private final Map cachedValues = new HashMap<>(); @@ -56,7 +65,7 @@ public class DfaBoxedValue extends DfaValue { } @Nullable - public DfaValue createBoxed(DfaValue valueToWrap) { + public DfaValue createBoxed(DfaValue valueToWrap, @Nullable PsiType type) { if (valueToWrap instanceof DfaVariableValue && ((DfaVariableValue)valueToWrap).getSource() == SpecialField.UNBOX) { return ((DfaVariableValue)valueToWrap).getQualifier(); } @@ -66,7 +75,7 @@ public class DfaBoxedValue extends DfaValue { if (o == null) return null; DfaBoxedValue boxedValue = cachedValues.get(o); if (boxedValue == null) { - cachedValues.put(o, boxedValue = new DfaBoxedValue(valueToWrap, myFactory)); + cachedValues.put(o, boxedValue = new DfaBoxedValue(valueToWrap, myFactory, type)); } return boxedValue; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java index 082de6f225c2..ee7a16e7cef7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaConstValue.java @@ -71,7 +71,7 @@ public class DfaConstValue extends DfaValue { Boolean boo = computeJavaLangBooleanFieldReference(variable); if (boo != null) { DfaConstValue unboxed = createFromValue(boo, PsiType.BOOLEAN); - return myFactory.getBoxedFactory().createBoxed(unboxed); + return myFactory.getBoxedFactory().createBoxed(unboxed, PsiType.BOOLEAN.getBoxedType(variable)); } PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(variable.getInitializer()); if (initializer instanceof PsiLiteralExpression && initializer.textMatches(PsiKeyword.NULL)) { diff --git a/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java b/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java index 2185aa0d1099..377d105e3814 100644 --- a/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java +++ b/java/java-tests/testData/inspection/dataFlow/ancient/CaseAndNpe.java @@ -68,7 +68,7 @@ public class aaa { a = 4; } - if (a != null && a instanceof aaa) { + if (a != null && a instanceof aaa) { if (true) { //a = new aaa(); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LargeIntegersAreNotEqualWhenBoxed.java b/java/java-tests/testData/inspection/dataFlow/fixture/LargeIntegersAreNotEqualWhenBoxed.java index 64e270cb666e..e20497df91e2 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/LargeIntegersAreNotEqualWhenBoxed.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LargeIntegersAreNotEqualWhenBoxed.java @@ -1,4 +1,10 @@ class X { + void test() { + Object obj1 = 100000L; + Object obj2 = 100000L; + if(obj1 == obj2) {} + } + public static void main(String[] args) { int primitive = 1024; Object object = primitive; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java index 11343b13eed3..0b44f28251d2 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/StringEquality.java @@ -90,4 +90,10 @@ class StringEquality { } } } + + void testObject() { + Object x = " foo ".trim(); + Object y = " foo ".trim(); + if (x == y) {} + } } \ No newline at end of file