diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java index f66a4261916e..e9c854bd7213 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CFGBuilder.java @@ -207,6 +207,16 @@ public class CFGBuilder { return add(new ObjectOfInstruction()); } + /** + * Generate instructions to bind top-of-stack value to the given expression. Stack remains unchanged. + * + * @param expression expression to bind top-of-stack value to + * @return this builder + */ + public CFGBuilder resultOf(PsiExpression expression) { + return add(new ResultOfInstruction(expression)); + } + /** * Generate instructions to perform an Class.isInstance operation *

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 d8c6fa317f63..b648dde79d63 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 @@ -1512,6 +1512,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1]))); } } + if (shortCircuit) { + addInstruction(new ResultOfInstruction(expression)); + } } @Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java index 10322b2db8c3..55a929705418 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInstructionVisitor.java @@ -243,6 +243,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor { } private static boolean shouldCollectBooleanResult(PsiExpression expression) { + if (expression instanceof PsiLiteralExpression) return false; PsiType type = expression.getType(); if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false; if (expression instanceof PsiPrefixExpression || expression instanceof PsiPolyadicExpression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java index d87af0faa17e..2ea578cb5e4c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InstructionVisitor.java @@ -147,6 +147,11 @@ public abstract class InstructionVisitor { return nextInstruction(instruction, runner, state); } + public DfaInstructionState[] visitResultOf(ResultOfInstruction instruction, DataFlowRunner runner, DfaMemoryState state) { + pushExpressionResult(state.pop(), instruction, state); + return nextInstruction(instruction, runner, state); + } + protected static DfaInstructionState[] nextInstruction(Instruction instruction, DataFlowRunner runner, DfaMemoryState memState) { return new DfaInstructionState[]{new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), memState)}; } 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 5385d4c24e53..40e2d26b289f 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 @@ -101,7 +101,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { checkNotNullable(memState, dfaSource, kind.problem(rValue)); } - memState.push(dfaDest); + pushExpressionResult(dfaDest, instruction, memState); flushArrayOnUnknownAssignment(instruction, runner.getFactory(), dfaDest, memState); return nextInstruction(instruction, runner, memState); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java index 1c0e3e06c80f..9328cae7a859 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java @@ -81,11 +81,10 @@ public class OptionalChainInliner implements CallInliner { .ifNotNull() .swap() // stack: .. optValue, elseValue .end() - .pop(); - }) - .register(OPTIONAL_OR_NULL, (builder, call) -> { - // no op! + .pop() + .resultOf(call); }) + .register(OPTIONAL_OR_NULL, CFGBuilder::resultOf) .register(OPTIONAL_OR_ELSE_GET, (builder, call) -> { PsiExpression fn = call.getArgumentList().getExpressions()[0]; builder @@ -94,7 +93,8 @@ public class OptionalChainInliner implements CallInliner { .ifNull() .pop() .invokeFunction(0, fn) - .end(); + .end() + .resultOf(call); }) .register(OPTIONAL_IF_PRESENT, (builder, call) -> { PsiExpression fn = call.getArgumentList().getExpressions()[0]; @@ -106,7 +106,8 @@ public class OptionalChainInliner implements CallInliner { .elseBranch() .pop() .pushUnknown() - .end(); + .end() + .resultOf(call); }); private static final CallMapper> INTERMEDIATE_MAPPER = diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/AssignInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/AssignInstruction.java index 9066a3a52f23..8826a33fda71 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/AssignInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/AssignInstruction.java @@ -24,10 +24,11 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.psi.PsiAssignmentExpression; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiVariable; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; -public class AssignInstruction extends Instruction { +public class AssignInstruction extends Instruction implements ExpressionPushingInstruction { private final PsiExpression myRExpression; private final PsiExpression myLExpression; @Nullable private final DfaValue myAssignedValue; @@ -70,6 +71,13 @@ public class AssignInstruction extends Instruction { return "ASSIGN"; } + @Nullable + @Override + public PsiAssignmentExpression getExpression() { + if(myRExpression== null) return null; + return ObjectUtils.tryCast(myRExpression.getParent(), PsiAssignmentExpression.class); + } + @Contract("null -> null") @Nullable private static PsiExpression getLeftHandOfAssignment(PsiExpression rExpression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ResultOfInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ResultOfInstruction.java new file mode 100644 index 000000000000..a240dfd7b84e --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ResultOfInstruction.java @@ -0,0 +1,34 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package com.intellij.codeInspection.dataFlow.instructions; + +import com.intellij.codeInspection.dataFlow.DataFlowRunner; +import com.intellij.codeInspection.dataFlow.DfaInstructionState; +import com.intellij.codeInspection.dataFlow.DfaMemoryState; +import com.intellij.codeInspection.dataFlow.InstructionVisitor; +import com.intellij.psi.PsiExpression; +import org.jetbrains.annotations.NotNull; + +public class ResultOfInstruction extends Instruction implements ExpressionPushingInstruction { + @NotNull + private final PsiExpression myExpression; + + public ResultOfInstruction(@NotNull PsiExpression expression) { + myExpression = expression; + } + + @Override + public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { + return visitor.visitResultOf(this, runner, stateBefore); + } + + public String toString() { + return "RESULT_OF "+myExpression.getText(); + } + + @NotNull + @Override + public PsiExpression getExpression() { + return myExpression; + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AndAndWithOr.java b/java/java-tests/testData/inspection/dataFlow/fixture/AndAndWithOr.java new file mode 100644 index 000000000000..62bc894f9812 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AndAndWithOr.java @@ -0,0 +1,9 @@ +import java.util.List; + +class Test { + void test(String type) { + if(type != null && (type.equals("foo") | type.equals("bar"))) { + System.out.println("Who knows"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/BoxingBoolean.java b/java/java-tests/testData/inspection/dataFlow/fixture/BoxingBoolean.java index ac14680e1333..2b02403f0de9 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/BoxingBoolean.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/BoxingBoolean.java @@ -41,7 +41,7 @@ class S { } public void te5(boolean b){ Boolean c = Boolean.TRUE; - boolean o = b||c; + boolean o = b||c; if (o) { } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/OrWithAssignment.java b/java/java-tests/testData/inspection/dataFlow/fixture/OrWithAssignment.java new file mode 100644 index 000000000000..3c854fea62da --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/OrWithAssignment.java @@ -0,0 +1,10 @@ +import java.util.List; + +class Test { + void test(String type) { + boolean uint = false; + if ("int".equals(type) || (uint = "uint".equals(type))) { + System.out.println("possible"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java index b857edd0bc3b..e8d7c909eb1c 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTest.java @@ -622,4 +622,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testEqualsInLoopNotTooComplex() { doTest(); } public void testEqualsWithItself() { doTest(); } public void testBoxingBoolean() { doTest(); } + public void testOrWithAssignment() { doTest(); } + public void testAndAndWithOr() { doTest(); } }