diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index ad3bd6d8fb92..1b9fb57d2987 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -18,10 +18,7 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.instructions.*; -import com.intellij.codeInspection.dataFlow.value.DfaUnknownValue; -import com.intellij.codeInspection.dataFlow.value.DfaValue; -import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; -import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; +import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.psi.*; @@ -702,6 +699,16 @@ class ControlFlowAnalyzer extends JavaElementVisitor { return NOT_FOUND; } + private static class ApplyNotNullInstruction extends Instruction { + @Override + public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState state, InstructionVisitor visitor) { + DfaValue value = state.pop(); + DfaValueFactory factory = runner.getFactory(); + state.applyCondition(factory.getRelationFactory().create(value, factory.getConstFactory().getNull(), JavaTokenType.EQEQ, true)); + return nextInstruction(runner, state); + } + } + class CatchDescriptor { private final PsiType myType; private PsiParameter myParameter; @@ -1182,6 +1189,21 @@ class ControlFlowAnalyzer extends JavaElementVisitor { if (!myCatchStack.isEmpty()) { addMethodThrows(expression.resolveMethod()); } + + if (paramExprs.length == 1 && method instanceof PsiMethod && + "equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 && + parameters[0].getType().equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && + PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType())) { + addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null)); + addInstruction(new SwapInstruction()); + addInstruction(new ConditionalGotoInstruction(getEndOffset(expression), true, null)); + + addInstruction(new PopInstruction()); + addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); + + paramExprs[0].accept(this); + addInstruction(new ApplyNotNullInstruction()); + } } finally { finishElement(expression); diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java index 2c831bbdc8dd..2db2e72ecf1c 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java @@ -34,7 +34,6 @@ import com.intellij.codeInspection.*; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.ex.BaseLocalInspectionTool; import com.intellij.ide.DataManager; -import com.intellij.lang.ASTFactory; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -42,7 +41,6 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.util.Pair; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; -import com.intellij.psi.impl.source.codeStyle.CodeFormatterFacade; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.ArrayUtil; @@ -71,11 +69,6 @@ public class DataFlowInspection extends BaseLocalInspectionTool { return new OptionsPanel(); } - void test(@NotNull List l) { - final List list = null; - test(list); - } - @NotNull public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index b38fa364fa43..6de8c43e0f79 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -507,6 +507,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } public boolean isNotNull(DfaVariableValue dfaVar) { + if (getVariableState(dfaVar).isNotNull()) { + return true; + } + DfaConstValue dfaNull = myFactory.getConstFactory().getNull(); Integer c1Index = getOrCreateEqClassIndex(dfaVar); Integer c2Index = getOrCreateEqClassIndex(dfaNull); diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 7effd14ca844..9c11f3900998 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -266,13 +266,21 @@ public class StandardInstructionVisitor extends InstructionVisitor { } boolean negated = memState.canBeNaN(dfaLeft) || memState.canBeNaN(dfaRight); - DfaRelationValue dfaRelation = factory.getRelationFactory().create(dfaLeft, dfaRight, opSign, negated); + DfaRelationValue.Factory relationFactory = factory.getRelationFactory(); + DfaRelationValue dfaRelation = relationFactory.create(dfaLeft, dfaRight, opSign, negated); if (dfaRelation != null) { myCanBeNullInInstanceof.add(instruction); ArrayList states = new ArrayList(); final DfaMemoryState trueCopy = memState.createCopy(); if (trueCopy.applyCondition(dfaRelation)) { + if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaVariableValue) { + if (trueCopy.isNotNull((DfaVariableValue)dfaLeft)) { + trueCopy.applyCondition(relationFactory.create(dfaRight, factory.getConstFactory().getNull(), JavaTokenType.EQEQ, true)); + } else if (trueCopy.isNotNull((DfaVariableValue)dfaRight)) { + trueCopy.applyCondition(relationFactory.create(dfaLeft, factory.getConstFactory().getNull(), JavaTokenType.EQEQ, true)); + } + } trueCopy.push(factory.getConstFactory().getTrue()); instruction.setTrueReachable(); states.add(new DfaInstructionState(next, trueCopy)); diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/EqualsNotNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/EqualsNotNull.java new file mode 100644 index 000000000000..9e2fbfd30163 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/EqualsNotNull.java @@ -0,0 +1,59 @@ +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Main { + @NotNull + private Object test1(@NotNull Object defVal, @Nullable final Object val) { + return defVal; + } + @NotNull + private Object test11(@NotNull Object defVal, @Nullable final Object val) { + if (val != null) { + return val; + } + return defVal; + } + @NotNull + private Object test5(@NotNull Object defVal, @Nullable final Object val) { + if (defVal == val) { + return val; + } + return defVal; + } + @NotNull + private Object test6(@NotNull Object defVal, @Nullable final Object val) { + if (val == defVal) { + return val; + } + return defVal; + } + @NotNull + private Object test7(@NotNull Object defVal, @Nullable final Object val) { + if (val.equals(defVal)) { + return defVal; + } + return defVal; + } + @NotNull + private Object test8(@NotNull Object defVal, @Nullable final Object val) { + if (defVal.equals(val)) { + return val; + } + return defVal; + } + @NotNull private Object test9(@NotNull Object defVal, @Nullable final Object val) { + if (equals(val)) { + return val; + } + return defVal; + } + @NotNull private Object test10(@NotNull Object defVal, @Nullable final Object val) { + if (val != null) { + return val; + } + if (defVal.equals(val)) { + return val; + } + return defVal; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java index f75ae34c2650..47b3f560eec1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionFixtureTest.java @@ -70,4 +70,6 @@ public class DataFlowInspectionFixtureTest extends JavaCodeInsightFixtureTestCas public void testMultiCatch() throws Throwable { doTest(); } public void testContinueFlushesLoopVariable() throws Throwable { doTest(); } + public void testEqualsNotNull() throws Throwable { doTest(); } + }