diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java
index 0459e035fcd5..6b4dc955d255 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java
@@ -564,11 +564,11 @@ public class GuessManagerImpl extends GuessManager {
@Override
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- if (myForPlace == instruction.getPlace()) {
+ if (myForPlace == instruction.getExpression()) {
addToResult(((ExpressionTypeMemoryState)memState).getStates());
}
DfaInstructionState[] states = super.visitPush(instruction, runner, memState);
- if (myForPlace == instruction.getPlace()) {
+ if (myForPlace == instruction.getExpression()) {
addConstraints(states);
}
return states;
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 ffd8d3bcd849..b0398fe710a8 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
@@ -125,6 +125,21 @@ public class CFGBuilder {
return add(new PushInstruction(value, null));
}
+ /**
+ * Generate instructions to push given DfaValue on stack and bind it to given expression.
+ *
+ * Stack before: ...
+ *
+ * Stack after: ... value
+ *
+ * @param value value to push
+ * @param expression expression which result is being pushed
+ * @return this builder
+ */
+ public CFGBuilder push(DfaValue value, PsiExpression expression) {
+ return add(new PushInstruction(value, expression));
+ }
+
/**
* Generate instructions to pop single DfaValue from stack
*
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java
index 86f70c6e7c12..882f97aa06cb 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CommonDataflow.java
@@ -1,17 +1,13 @@
// 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;
-import com.intellij.codeInspection.dataFlow.instructions.*;
+import com.intellij.codeInspection.dataFlow.instructions.EndOfInitializerInstruction;
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
-import com.intellij.psi.util.CachedValueProvider;
-import com.intellij.psi.util.CachedValuesManager;
-import com.intellij.psi.util.PsiModificationTracker;
-import com.intellij.psi.util.PsiUtil;
+import com.intellij.psi.util.*;
import com.intellij.util.JavaPsiConstructorUtil;
-import com.intellij.util.ObjectUtils;
-import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -42,6 +38,12 @@ public class CommonDataflow {
newMap = newMap.with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL);
}
myFacts.put(expression, existing == null ? newMap : existing.union(newMap));
+
+ PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
+ if (parent instanceof PsiConditionalExpression &&
+ !PsiTreeUtil.isAncestor(((PsiConditionalExpression)parent).getCondition(), expression, false)) {
+ add((PsiExpression)parent, memState, value);
+ }
}
}
@@ -166,76 +168,14 @@ public class CommonDataflow {
}
@Override
- public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- DfaInstructionState[] states = super.visitPush(instruction, runner, memState);
- PsiExpression place = instruction.getPlace();
- if (place != null && !instruction.isReferenceWrite()) {
- for (DfaInstructionState state : states) {
- DfaMemoryState afterState = state.getMemoryState();
- myResult.add(place, (DfaMemoryStateImpl)afterState, instruction.getValue());
- }
+ public void beforeExpressionPush(@NotNull DfaValue value,
+ @NotNull PsiExpression expression,
+ @Nullable TextRange range,
+ @NotNull DfaMemoryState state) {
+ if (range == null && value != myFail) {
+ // Do not track instructions which cover part of expression
+ myResult.add(expression, (DfaMemoryStateImpl)state, value);
}
- return states;
- }
-
- @Override
- public DfaInstructionState[] visitArrayAccess(ArrayAccessInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- DfaInstructionState[] states = super.visitArrayAccess(instruction, runner, memState);
- PsiArrayAccessExpression anchor = instruction.getExpression();
- for (DfaInstructionState state : states) {
- DfaMemoryState afterState = state.getMemoryState();
- myResult.add(anchor, (DfaMemoryStateImpl)afterState, afterState.peek());
- }
- return states;
- }
-
- @Override
- public DfaInstructionState[] visitBinop(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- DfaInstructionState[] states = super.visitBinop(instruction, runner, memState);
- PsiElement anchor = instruction.getPsiAnchor();
- if (anchor instanceof PsiExpression) {
- for (DfaInstructionState state : states) {
- DfaMemoryState afterState = state.getMemoryState();
- myResult.add((PsiExpression)anchor, (DfaMemoryStateImpl)afterState, afterState.peek());
- }
- }
- return states;
- }
-
- @NotNull
- @Override
- protected DfaCallArguments popCall(MethodCallInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState,
- boolean contractOnly) {
- DfaCallArguments arguments = super.popCall(instruction, runner, memState, contractOnly);
- PsiElement context = instruction.getContext();
- if (instruction.getMethodType() == MethodCallInstruction.MethodType.REGULAR_METHOD_CALL &&
- context instanceof PsiMethodCallExpression) {
- PsiExpression qualifier =
- PsiUtil.skipParenthesizedExprDown(((PsiMethodCallExpression)context).getMethodExpression().getQualifierExpression());
- if (qualifier != null) {
- myResult.add(qualifier, (DfaMemoryStateImpl)memState, arguments.myQualifier);
- }
- }
- return arguments;
- }
-
- @Override
- public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState) {
- DfaInstructionState[] states = super.visitMethodCall(instruction, runner, memState);
- PsiExpression context = ObjectUtils.tryCast(instruction.getContext(), PsiExpression.class);
- if (context != null && ExpressionUtils.getCallForQualifier(context) == null) {
- for (DfaInstructionState state : states) {
- DfaValue value = state.getMemoryState().peek();
- if (value != myFail) {
- myResult.add(context, (DfaMemoryStateImpl)state.getMemoryState(), value);
- }
- }
- }
- return states;
}
}
}
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 e60f8e7e5183..071e2b95b199 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
@@ -1477,8 +1477,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
operand = operands[i];
operand.accept(this);
generateBoxingUnboxingInstructionFor(operand, exprType);
- PsiElement psiAnchor = i == operands.length - 1 && expression.isPhysical() ? expression : null;
- addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, exprType));
+ PsiExpression psiAnchor = expression.isPhysical() ? expression : null;
+ addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, exprType, i));
}
}
@@ -1955,7 +1955,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new AssignInstruction(operand, null, myFactory.createValue(operand)));
}
else if (expression.getOperationTokenType() == JavaTokenType.EXCL) {
- addInstruction(new NotInstruction());
+ addInstruction(new NotInstruction(expression));
}
else if (expression.getOperationTokenType() == JavaTokenType.MINUS && (PsiType.INT.equals(type) || PsiType.LONG.equals(type))) {
addInstruction(new PushInstruction(myFactory.getConstFactory().createDefault(type), null));
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
index a8f212296a4e..db11486872b6 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java
@@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import static com.intellij.psi.CommonClassNames.*;
@@ -53,12 +52,14 @@ class CustomMethodHandlers {
interface CustomMethodHandler {
- List handle(DfaCallArguments callArguments, DfaMemoryState memState, DfaValueFactory factory);
+ @Nullable
+ DfaValue getMethodResult(DfaCallArguments callArguments, DfaMemoryState memState, DfaValueFactory factory);
+
default CustomMethodHandler compose(CustomMethodHandler other) {
if (other == null) return this;
return (args, memState, factory) -> {
- List result = this.handle(args, memState, factory);
- return result.isEmpty() ? other.handle(args, memState, factory) : result;
+ DfaValue result = this.getMethodResult(args, memState, factory);
+ return result == null ? other.getMethodResult(args, memState, factory) : result;
};
}
@@ -71,16 +72,15 @@ class CustomMethodHandlers {
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
(args, memState, factory) -> mathAbs(args.myArguments, memState, factory, false))
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("long"),
- (args, memState, factory) -> mathAbs(args.myArguments, memState, factory, true));
+ (args, memState, factory) -> mathAbs(args.myArguments, memState, factory, true))
+ .register(DfaOptionalSupport.OPTIONAL_OF_NULLABLE,
+ (args, memState, factory) -> ofNullable(args.myArguments[0], memState, factory));
public static CustomMethodHandler find(MethodCallInstruction instruction) {
PsiMethod method = instruction.getTargetMethod();
CustomMethodHandler handler = null;
if (isConstantCall(method)) {
- handler = (args, memState, factory) -> {
- DfaValue value = handleConstantCall(args, memState, factory, method);
- return value == null ? Collections.emptyList() : singleResult(memState, value);
- };
+ handler = (args, memState, factory) -> handleConstantCall(args, memState, factory, method);
}
CustomMethodHandler handler2 = CUSTOM_METHOD_HANDLERS.mapFirst(method);
return handler == null ? handler2 : handler.compose(handler2);
@@ -190,27 +190,32 @@ class CustomMethodHandlers {
});
}
- private static List indexOf(DfaValue qualifier,
- DfaMemoryState memState,
- DfaValueFactory factory,
- SpecialField specialField) {
+ private static DfaValue indexOf(DfaValue qualifier,
+ DfaMemoryState memState,
+ DfaValueFactory factory,
+ SpecialField specialField) {
DfaValue length = specialField.createValue(factory, qualifier);
LongRangeSet range = memState.getValueFact(length, DfaFactType.RANGE);
long maxLen = range == null || range.isEmpty() ? Integer.MAX_VALUE : range.max();
- return singleResult(memState, factory.getFactValue(DfaFactType.RANGE, LongRangeSet.range(-1, maxLen - 1)));
+ return factory.getFactValue(DfaFactType.RANGE, LongRangeSet.range(-1, maxLen - 1));
}
- private static List mathAbs(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean isLong) {
+ private static DfaValue ofNullable(DfaValue argument, DfaMemoryState state, DfaValueFactory factory) {
+ if (state.isNull(argument)) {
+ return factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false);
+ }
+ if (state.isNotNull(argument)) {
+ return factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, true);
+ }
+ return null;
+ }
+
+ private static DfaValue mathAbs(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean isLong) {
DfaValue arg = ArrayUtil.getFirstElement(args);
- if(arg == null) return Collections.emptyList();
+ if (arg == null) return null;
LongRangeSet range = memState.getValueFact(arg, DfaFactType.RANGE);
- if (range == null) return Collections.emptyList();
- return singleResult(memState, factory.getFactValue(DfaFactType.RANGE, range.abs(isLong)));
- }
-
- private static List singleResult(DfaMemoryState state, DfaValue value) {
- state.push(value);
- return Collections.singletonList(state);
+ if (range == null) return null;
+ return factory.getFactValue(DfaFactType.RANGE, range.abs(isLong));
}
private static Object getConstantValue(DfaMemoryState memoryState, DfaValue value) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
index e01af5243262..4c3f59693008 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java
@@ -482,7 +482,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
Set reportedAnchors) {
for (Instruction instruction : runner.getInstructions()) {
if (instruction instanceof PushInstruction) {
- PsiExpression place = ((PushInstruction)instruction).getPlace();
+ PsiExpression place = ((PushInstruction)instruction).getExpression();
DfaValue value = ((PushInstruction)instruction).getValue();
Object constant = value instanceof DfaConstValue ? ((DfaConstValue)value).getValue() : null;
if (place instanceof PsiPolyadicExpression && constant instanceof Boolean && !isFlagCheck(place) && reportedAnchors.add(place)) {
@@ -494,20 +494,16 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
private static void reportOptionalOfNullableImprovements(ProblemsHolder holder,
Set reportedAnchors,
- Map nullArgs) {
- nullArgs.forEach((call, nullArg) -> {
- PsiElement arg = call.getArgumentAnchor(0);
- if (reportedAnchors.add(arg)) {
- switch (nullArg) {
- case YES:
- holder.registerProblem(arg, "Passing null argument to Optional",
- DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(arg));
- break;
- case NO:
- holder.registerProblem(arg, "Passing a non-null argument to Optional",
- DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix(arg));
- break;
- default:
+ Map nullArgs) {
+ nullArgs.forEach((anchor, alwaysPresent) -> {
+ if (alwaysPresent == ThreeState.UNSURE) return;
+ if (reportedAnchors.add(anchor)) {
+ if (alwaysPresent.toBoolean()) {
+ holder.registerProblem(anchor, "Passing a non-null argument to Optional",
+ DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix(anchor));
+ } else {
+ holder.registerProblem(anchor, "Passing null argument to Optional",
+ DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(anchor));
}
}
});
@@ -658,10 +654,12 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
}
}
- else if (psiAnchor != null && !reportedAnchors.contains(psiAnchor) && !isFlagCheck(psiAnchor)) {
+ else if (psiAnchor != null && !isFlagCheck(psiAnchor)) {
boolean evaluatesToTrue = trueSet.contains(instruction);
final PsiElement parent = psiAnchor.getParent();
- if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getLExpression() == psiAnchor) {
+ if (parent instanceof PsiAssignmentExpression &&
+ ((PsiAssignmentExpression)parent).getLExpression() == psiAnchor &&
+ reportedAnchors.add(psiAnchor)) {
holder.registerProblem(
psiAnchor,
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
@@ -669,19 +667,17 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
);
}
else {
- if (instruction instanceof BinopInstruction) {
- TextRange range = ((BinopInstruction)instruction).getAnchorRange();
- if (range != null) {
- // report rare cases like a == b == c where "a == b" part is constant
- String message = InspectionsBundle.message("dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
- holder.registerProblem(psiAnchor, range, message);
- // do not add to reported anchors if only part of expression was reported
- return;
- }
+ TextRange range =
+ instruction instanceof ExpressionPushingInstruction ? ((ExpressionPushingInstruction)instruction).getExpressionRange() : null;
+ if (range != null) {
+ // report rare cases like a == b == c where "a == b" part is constant
+ String message = InspectionsBundle.message("dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
+ holder.registerProblem(psiAnchor, range, message);
+ // do not add to reported anchors if only part of expression was reported
+ } else if (reportedAnchors.add(psiAnchor)) {
+ reportConstantCondition(holder, psiAnchor, evaluatesToTrue);
}
- reportConstantCondition(holder, psiAnchor, evaluatesToTrue);
}
- reportedAnchors.add(psiAnchor);
}
}
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 c18324c0f50d..4bfc6fc1d15e 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
@@ -5,6 +5,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
@@ -27,7 +28,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
private final Set myCCEInstructions = ContainerUtil.newHashSet();
private final Map myFailingCalls = new HashMap<>();
private final Map myBooleanCalls = new HashMap<>();
- private final Map myOfNullableCalls = new HashMap<>();
+ private final Map myOfNullableCalls = new HashMap<>();
private final Map> myArrayStoreProblems = new HashMap<>();
private final Map myMethodReferenceResults = new HashMap<>();
private final Map myOutOfBoundsArrayAccesses = new HashMap<>();
@@ -123,7 +124,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
return myArrayStoreProblems;
}
- Map getOfNullableCalls() {
+ Map getOfNullableCalls() {
return myOfNullableCalls;
}
@@ -161,17 +162,37 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
ContainerUtil.exists(instructions, i -> i instanceof ReturnInstruction && ((ReturnInstruction)i).getAnchor() instanceof PsiReturnStatement);
}
+ @Override
+ public void beforeExpressionPush(@NotNull DfaValue value,
+ @NotNull PsiExpression expression,
+ @Nullable TextRange range,
+ @NotNull DfaMemoryState memState) {
+ PsiElement anchor = extractOptionalOfNullableAnchor(expression);
+ if (anchor != null) {
+ Boolean fact = memState.getValueFact(value, DfaFactType.OPTIONAL_PRESENCE);
+ ThreeState present = fact == null ? ThreeState.UNSURE : ThreeState.fromBoolean(fact);
+ myOfNullableCalls.merge(anchor, present, ThreeState::merge);
+ }
+ }
+
+ private static PsiElement extractOptionalOfNullableAnchor(PsiExpression expression) {
+ if (expression instanceof PsiMethodCallExpression &&
+ DfaOptionalSupport.OPTIONAL_OF_NULLABLE.test((PsiMethodCallExpression)expression)) {
+ return ((PsiMethodCallExpression)expression).getArgumentList().getExpressions()[0];
+ }
+ if (expression instanceof PsiMethodReferenceExpression) {
+ PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)expression;
+ if (DfaOptionalSupport.OPTIONAL_OF_NULLABLE.methodReferenceMatches(methodRef)) {
+ return methodRef.getReferenceNameElement();
+ }
+ }
+ return null;
+ }
+
@Override
public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction,
DataFlowRunner runner,
DfaMemoryState memState) {
- if (instruction.matches(DfaOptionalSupport.OPTIONAL_OF_NULLABLE)) {
- DfaValue arg = memState.peek();
- ThreeState nullArg = memState.isNull(arg) ? ThreeState.YES : memState.isNotNull(arg) ? ThreeState.NO : ThreeState.UNSURE;
- // Passing variable with unknown nullity to ofNullable assumes that it can be null
- memState.applyFact(arg, DfaFactType.NULLABILITY, DfaNullability.NULLABLE);
- myOfNullableCalls.merge(instruction, nullArg, ThreeState::merge);
- }
DfaInstructionState[] states = super.visitMethodCall(instruction, runner, memState);
if (hasNonTrivialFailingContracts(instruction)) {
DfaConstValue fail = runner.getFactory().getConstFactory().getContractFail();
@@ -226,7 +247,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
@Override
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- PsiExpression place = instruction.getPlace();
+ PsiExpression place = instruction.getExpression();
if (!instruction.isReferenceWrite() && place instanceof PsiReferenceExpression) {
DfaValue dfaValue = instruction.getValue();
if (dfaValue instanceof DfaVariableValue) {
@@ -253,7 +274,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
if (values.size() == 1) {
Object singleValue = values.iterator().next();
if (singleValue != ANY_VALUE) {
- result.add(Pair.create((PsiReferenceExpression)instruction.getPlace(), (DfaConstValue)singleValue));
+ result.add(Pair.create((PsiReferenceExpression)instruction.getExpression(), (DfaConstValue)singleValue));
}
}
}
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 266f27ea27e2..cdeef9777f23 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
@@ -18,7 +18,6 @@ import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
-import com.intellij.ui.treeStructure.NullNode;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FList;
@@ -381,7 +380,7 @@ public class DfaUtil {
@Override
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- PsiExpression place = instruction.getPlace();
+ PsiExpression place = instruction.getExpression();
if (place != null) {
PlaceResult result = myResults.computeIfAbsent(place, __ -> new PlaceResult());
((ValuableDataFlowRunner.MyDfaMemoryState)memState).forVariableStates((variableValue, value) -> {
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 7f90e1f2fa17..a23a7fcff901 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
@@ -18,12 +18,14 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiArrayAccessExpression;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
@@ -32,6 +34,23 @@ import java.util.ArrayList;
*/
public abstract class InstructionVisitor {
+ public void beforeExpressionPush(@NotNull DfaValue value,
+ @NotNull PsiExpression expression,
+ @Nullable TextRange range,
+ @NotNull DfaMemoryState state) {
+
+ }
+
+ void pushExpressionResult(@NotNull DfaValue value,
+ @NotNull ExpressionPushingInstruction instruction,
+ @NotNull DfaMemoryState state) {
+ PsiExpression anchor = instruction.getExpression();
+ if (anchor != null && !(instruction instanceof PushInstruction && ((PushInstruction)instruction).isReferenceWrite())) {
+ beforeExpressionPush(value, anchor, instruction.getExpressionRange(), state);
+ }
+ state.push(value);
+ }
+
public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
memState.pop();
DfaValue dest = memState.pop();
@@ -89,7 +108,7 @@ public abstract class InstructionVisitor {
public DfaInstructionState[] visitBinop(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
memState.pop();
memState.pop();
- memState.push(DfaUnknownValue.getInstance());
+ pushExpressionResult(DfaUnknownValue.getInstance(), instruction, memState);
return nextInstruction(instruction, runner, memState);
}
@@ -183,7 +202,7 @@ public abstract class InstructionVisitor {
}
memState.pop(); //qualifier
- memState.push(DfaUnknownValue.getInstance());
+ pushExpressionResult(DfaUnknownValue.getInstance(), instruction, memState);
return nextInstruction(instruction, runner, memState);
}
@@ -195,19 +214,19 @@ public abstract class InstructionVisitor {
DfaValue dfaValue = memState.pop();
dfaValue = dfaValue.createNegated();
- memState.push(dfaValue);
+ pushExpressionResult(dfaValue, instruction, memState);
return nextInstruction(instruction, runner, memState);
}
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
- memState.push(instruction.getValue());
+ pushExpressionResult(instruction.getValue(), instruction, memState);
return nextInstruction(instruction, runner, memState);
}
public DfaInstructionState[] visitArrayAccess(ArrayAccessInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
memState.pop(); // index
memState.pop(); // array reference
- memState.push(instruction.getValue());
+ pushExpressionResult(instruction.getValue(), instruction, memState);
return nextInstruction(instruction, runner, 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 f47acd58353c..eebd4814627f 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
@@ -195,7 +195,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (arrayElementValue != DfaUnknownValue.getInstance()) {
result = arrayElementValue;
}
- memState.push(result);
+ pushExpressionResult(result, instruction, memState);
return nextInstruction(instruction, runner, memState);
}
@@ -234,7 +234,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (contracts.isEmpty()) return;
PsiType returnType = substitutor.substitute(method.getReturnType());
DfaValue defaultResult = runner.getFactory().createTypeValue(returnType, DfaPsiUtil.getElementNullability(returnType, method));
- Stream returnValues = possibleReturnValues(callArguments, state, contracts, runner.getFactory(), defaultResult);
+ Stream returnValues = possibleReturnValues(callArguments, state, contracts, runner.getFactory(), defaultResult, methodRef);
returnValues.forEach(res -> processMethodReferenceResult(methodRef, contracts, res));
}
@@ -268,14 +268,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return new DfaCallArguments(qualifier, arguments, JavaMethodContractUtil.isPure(method));
}
- private static Stream possibleReturnValues(DfaCallArguments callArguments,
- DfaMemoryState state,
- List extends MethodContract> contracts,
- DfaValueFactory factory, DfaValue defaultResult) {
+ private Stream possibleReturnValues(DfaCallArguments callArguments,
+ DfaMemoryState state,
+ List extends MethodContract> contracts,
+ DfaValueFactory factory,
+ DfaValue defaultResult,
+ PsiMethodReferenceExpression methodRef) {
Set currentStates = Collections.singleton(new DfaCallState(state.createClosureState(), callArguments));
Set finalStates = ContainerUtil.newLinkedHashSet();
for (MethodContract contract : contracts) {
- currentStates = addContractResults(contract, currentStates, factory, finalStates, defaultResult);
+ currentStates = addContractResults(contract, currentStates, factory, finalStates, defaultResult, methodRef);
}
return StreamEx.of(finalStates).map(DfaMemoryState::peek)
.append(currentStates.isEmpty() ? StreamEx.empty() : StreamEx.of(defaultResult)).distinct();
@@ -316,7 +318,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
DfaValue defaultResult = getMethodResultValue(instruction, callArguments.myQualifier, memState, runner.getFactory());
if (callArguments.myArguments != null) {
for (MethodContract contract : instruction.getContracts()) {
- currentStates = addContractResults(contract, currentStates, runner.getFactory(), finalStates, defaultResult);
+ currentStates = addContractResults(contract, currentStates, runner.getFactory(), finalStates, defaultResult, instruction.getExpression());
if (currentStates.size() + finalStates.size() > DataFlowRunner.MAX_STATES_PER_BRANCH) {
if (LOG.isDebugEnabled()) {
LOG.debug("Too complex contract on " + instruction.getContext() + ", skipping contract processing");
@@ -328,7 +330,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
}
for (DfaCallState callState : currentStates) {
- callState.myMemoryState.push(defaultResult);
+ pushExpressionResult(defaultResult, instruction, callState.myMemoryState);
finalStates.add(callState.myMemoryState);
}
}
@@ -356,8 +358,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (handler == null) return Collections.emptyList();
memState = memState.createCopy();
DfaCallArguments callArguments = popCall(instruction, runner, memState, false);
- return callArguments.myArguments == null ? Collections.emptyList() :
- handler.handle(callArguments, memState, runner.getFactory());
+ DfaValue result = callArguments.myArguments == null ? null : handler.getMethodResult(callArguments, memState, runner.getFactory());
+ if (result != null) {
+ pushExpressionResult(result, instruction, memState);
+ return Collections.singletonList(memState);
+ }
+ return Collections.emptyList();
}
@NotNull
@@ -447,15 +453,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return value;
}
- private static Set addContractResults(MethodContract contract,
- Set states,
- DfaValueFactory factory,
- Set finalStates,
- DfaValue defaultResult) {
+ private Set addContractResults(MethodContract contract,
+ Set states,
+ DfaValueFactory factory,
+ Set finalStates,
+ DfaValue defaultResult,
+ PsiExpression expression) {
if(contract.isTrivial()) {
for (DfaCallState callState : states) {
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, callState);
- callState.myMemoryState.push(result);
+ pushExpressionResult(result, () -> expression, callState.myMemoryState);
finalStates.add(callState.myMemoryState);
}
return Collections.emptySet();
@@ -484,7 +491,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
if(state != null) {
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, new DfaCallState(state, arguments));
- state.push(result);
+ pushExpressionResult(result, () -> expression, state);
finalStates.add(state);
}
}
@@ -632,7 +639,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return states;
}
}
- DfaValue result = null;
+ DfaValue result = DfaUnknownValue.getInstance();
PsiType type = instruction.getResultType();
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
@@ -644,10 +651,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
}
}
- if (result == null && JavaTokenType.PLUS == opSign && TypeUtils.isJavaLangString(type)) {
+ if (result == DfaUnknownValue.getInstance() && JavaTokenType.PLUS == opSign && TypeUtils.isJavaLangString(type)) {
result = runner.getFactory().createTypeValue(type, Nullability.NOT_NULL);
}
- memState.push(result == null ? DfaUnknownValue.getInstance() : result);
+ pushExpressionResult(result, instruction, memState);
instruction.setTrueReachable(); // Not a branching instruction actually.
instruction.setFalseReachable();
@@ -656,12 +663,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
@Nullable
- private static DfaInstructionState[] handleRelationBinop(BinopInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState,
- DfaValue dfaRight,
- DfaValue dfaLeft,
- RelationType relationType) {
+ private DfaInstructionState[] handleRelationBinop(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ DfaValue dfaRight,
+ DfaValue dfaLeft,
+ RelationType relationType) {
DfaValueFactory factory = runner.getFactory();
RelationType[] relations = splitRelation(relationType);
@@ -769,11 +776,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
@Nullable
- private static DfaInstructionState[] handleConstantComparison(BinopInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState,
- DfaValue dfaRight,
- DfaValue dfaLeft, RelationType relationType) {
+ private DfaInstructionState[] handleConstantComparison(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ DfaValue dfaRight,
+ DfaValue dfaLeft, RelationType relationType) {
if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaVariableValue) {
Number leftValue = getKnownNumberValue(memState, (DfaVariableValue)dfaLeft);
Number rightValue = getKnownNumberValue(memState, (DfaVariableValue)dfaRight);
@@ -812,11 +819,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
}
@Nullable
- private static DfaInstructionState[] checkComparingWithConstant(BinopInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState,
- DfaVariableValue var,
- RelationType opSign, Number comparedWith) {
+ private DfaInstructionState[] checkComparingWithConstant(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ DfaVariableValue var,
+ RelationType opSign, Number comparedWith) {
Number knownValue = getKnownNumberValue(memState, var);
if (knownValue != null) {
return checkComparisonWithKnownValue(instruction, runner, memState, opSign, knownValue, comparedWith);
@@ -830,12 +837,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return knownConstantValue != null && knownConstantValue.getValue() instanceof Number ? (Number)knownConstantValue.getValue() : null;
}
- private static DfaInstructionState[] checkComparisonWithKnownValue(BinopInstruction instruction,
- DataFlowRunner runner,
- DfaMemoryState memState,
- RelationType opSign,
- Number leftValue,
- Number rightValue) {
+ private DfaInstructionState[] checkComparisonWithKnownValue(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ RelationType opSign,
+ Number leftValue,
+ Number rightValue) {
int cmp = compare(leftValue, rightValue);
Boolean result = null;
boolean hasNaN = DfaUtil.isNaN(leftValue) || DfaUtil.isNaN(rightValue);
@@ -869,12 +876,19 @@ public class StandardInstructionVisitor extends InstructionVisitor {
return Double.compare(a.doubleValue(), b.doubleValue());
}
- private static DfaInstructionState[] makeBooleanResultArray(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState, boolean result) {
+ private DfaInstructionState[] makeBooleanResultArray(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ boolean result) {
return new DfaInstructionState[]{makeBooleanResult(instruction, runner, memState, ThreeState.fromBoolean(result))};
}
- private static DfaInstructionState makeBooleanResult(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState, @NotNull ThreeState result) {
- memState.push(result == ThreeState.UNSURE ? DfaUnknownValue.getInstance() : runner.getFactory().getBoolean(result.toBoolean()));
+ private DfaInstructionState makeBooleanResult(BinopInstruction instruction,
+ DataFlowRunner runner,
+ DfaMemoryState memState,
+ @NotNull ThreeState result) {
+ DfaValue value = result == ThreeState.UNSURE ? DfaUnknownValue.getInstance() : runner.getFactory().getBoolean(result.toBoolean());
+ pushExpressionResult(value, instruction, memState);
if (result != ThreeState.NO) {
instruction.setTrueReachable();
}
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 2c1b2e355788..1c0e3e06c80f 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
@@ -256,14 +256,23 @@ public class OptionalChainInliner implements CallInliner {
private static void inlineOf(CFGBuilder builder, PsiType optionalElementType, PsiMethodCallExpression qualifierCall) {
PsiExpression argument = qualifierCall.getArgumentList().getExpressions()[0];
- builder.pushExpression(argument)
- .boxUnbox(argument, optionalElementType)
- .pushUnknown() // ... arg, ?
- .splice(2, 1, 0, 1) // ... arg, ?, arg
- .invoke(qualifierCall) // ... arg, opt -- keep original call in CFG so some warnings like "ofNullable for null" can work
- .pop(); // ... arg
+ builder
+ .pushExpression(argument)
+ .boxUnbox(argument, optionalElementType);
if ("of".equals(qualifierCall.getMethodExpression().getReferenceName())) {
- builder.checkNotNull(argument, NullabilityProblemKind.passingNullableToNotNullParameter);
+ builder.checkNotNull(argument, NullabilityProblemKind.passingNullableToNotNullParameter)
+ .push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true), qualifierCall)
+ .pop();
+ }
+ else {
+ builder
+ .dup()
+ .ifNull()
+ .push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, false), qualifierCall)
+ .elseBranch()
+ .push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true), qualifierCall)
+ .end()
+ .pop();
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java
index 5f1b998b11bf..1c218a1dc84d 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java
@@ -169,8 +169,10 @@ public class StreamChainInliner implements CallInliner {
myNext.pushResult(builder);
}
else {
- builder.push(builder.getFactory()
- .createTypeValue(myCall.getType(), DfaPsiUtil.getElementNullability(myCall.getType(), myCall.resolveMethod())));
+ DfaValue resultValue =
+ builder.getFactory().createTypeValue(myCall.getType(),
+ DfaPsiUtil.getElementNullability(myCall.getType(), myCall.resolveMethod()));
+ builder.push(resultValue, myCall);
}
}
@@ -220,7 +222,7 @@ public class StreamChainInliner implements CallInliner {
@Override
void pushResult(CFGBuilder builder) {
- builder.push(myResult);
+ builder.push(myResult, myCall);
}
}
@@ -655,7 +657,7 @@ public class StreamChainInliner implements CallInliner {
.ifConditionIs(true)
.chain(b -> buildStreamCFG(b, firstStep, originalQualifier))
.end()
- .push(builder.getFactory().createTypeValue(call.getType(), Nullability.NOT_NULL));
+ .push(builder.getFactory().createTypeValue(call.getType(), Nullability.NOT_NULL), call);
return true;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ArrayAccessInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ArrayAccessInstruction.java
index f6731b89308a..b926a805dc91 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ArrayAccessInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ArrayAccessInstruction.java
@@ -23,7 +23,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.PsiArrayAccessExpression;
import org.jetbrains.annotations.NotNull;
-public class ArrayAccessInstruction extends Instruction {
+public class ArrayAccessInstruction extends Instruction implements ExpressionPushingInstruction {
private final @NotNull DfaValue myValue;
private final @NotNull PsiArrayAccessExpression myExpression;
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 6741bd4bfdf1..6960b25103c7 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
@@ -21,7 +21,6 @@ import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
import com.intellij.openapi.util.TextRange;
-import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiPolyadicExpression;
import com.intellij.psi.PsiType;
@@ -31,18 +30,18 @@ import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.JavaTokenType.*;
-public class BinopInstruction extends BranchingInstruction {
+public class BinopInstruction extends BranchingInstruction implements ExpressionPushingInstruction {
private static final TokenSet ourSignificantOperations =
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, GTGT, GTGTGT);
private final IElementType myOperationSign;
private final @Nullable PsiType myResultType;
private final int myLastOperand;
- public BinopInstruction(IElementType opSign, @Nullable PsiElement psiAnchor, @Nullable PsiType resultType) {
+ public BinopInstruction(IElementType opSign, @Nullable PsiExpression psiAnchor, @Nullable PsiType resultType) {
this(opSign, psiAnchor, resultType, -1);
}
- public BinopInstruction(IElementType opSign, @Nullable PsiElement psiAnchor, @Nullable PsiType resultType, int lastOperand) {
+ public BinopInstruction(IElementType opSign, @Nullable PsiExpression psiAnchor, @Nullable PsiType resultType, int lastOperand) {
super(psiAnchor);
myResultType = resultType;
myOperationSign = ourSignificantOperations.contains(opSign) ? opSign : null;
@@ -53,7 +52,7 @@ public class BinopInstruction extends BranchingInstruction {
* @return range inside the anchor which evaluates this instruction, or null if the whole anchor evaluates this instruction
*/
@Nullable
- public TextRange getAnchorRange() {
+ public TextRange getExpressionRange() {
if (myLastOperand != -1 && getPsiAnchor() instanceof PsiPolyadicExpression) {
PsiPolyadicExpression anchor = (PsiPolyadicExpression)getPsiAnchor();
PsiExpression[] operands = anchor.getOperands();
@@ -64,6 +63,12 @@ public class BinopInstruction extends BranchingInstruction {
return null;
}
+ @Nullable
+ @Override
+ public PsiExpression getExpression() {
+ return (PsiExpression)getPsiAnchor();
+ }
+
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
return visitor.visitBinop(this, runner, stateBefore);
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ExpressionPushingInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ExpressionPushingInstruction.java
new file mode 100644
index 000000000000..b2605afd260b
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ExpressionPushingInstruction.java
@@ -0,0 +1,25 @@
+// 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.openapi.util.TextRange;
+import com.intellij.psi.PsiExpression;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * An instruction which pushes a result of {@link PsiExpression} (or its part) evaluation to the stack
+ *
+ */
+public interface ExpressionPushingInstruction {
+ /**
+ * @return a PsiExpression which result is pushed to the stack, or null if this instruction is not bound to any particular PsiExpression
+ */
+ @Nullable
+ PsiExpression getExpression();
+
+ /**
+ * @return if non-null, a part of PsiExpression, returned by {@link #getExpression()} which this instruction actually evaluates.
+ * Usable for polyadic expressions like {@code a == b == c}: here instruction may evaluate only {@code a == b} part.
+ */
+ @Nullable
+ default TextRange getExpressionRange() {return null;}
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java
index 2631084367e6..01b51c0cce90 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/InstanceofInstruction.java
@@ -19,7 +19,10 @@ 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.*;
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiMethodCallExpression;
+import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,7 +33,7 @@ public class InstanceofInstruction extends BinopInstruction {
@Nullable private final PsiExpression myLeft;
@Nullable private final PsiType myCastType;
- public InstanceofInstruction(PsiElement psiAnchor, @Nullable PsiExpression left, @NotNull PsiType castType) {
+ public InstanceofInstruction(PsiExpression psiAnchor, @Nullable PsiExpression left, @NotNull PsiType castType) {
super(JavaTokenType.INSTANCEOF_KEYWORD, psiAnchor, PsiType.BOOLEAN);
myLeft = left;
myCastType = castType;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java
index 3ae7a6bf8116..51344892dbd8 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/MethodCallInstruction.java
@@ -21,14 +21,13 @@ import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.*;
import com.intellij.util.ObjectUtils;
-import com.siyeh.ig.callMatcher.CallMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
-public class MethodCallInstruction extends Instruction {
+public class MethodCallInstruction extends Instruction implements ExpressionPushingInstruction {
private static final Nullability[] EMPTY_NULLABILITY_ARRAY = new Nullability[0];
@Nullable private final PsiType myType;
@@ -120,15 +119,10 @@ public class MethodCallInstruction extends Instruction {
myReturnNullability = call instanceof PsiNewExpression ? Nullability.NOT_NULL : DfaPsiUtil.getElementNullability(myType, myTargetMethod);
}
- public boolean matches(CallMatcher matcher) {
- switch (myMethodType) {
- case REGULAR_METHOD_CALL:
- return myContext instanceof PsiMethodCallExpression && matcher.test((PsiMethodCallExpression)myContext);
- case METHOD_REFERENCE_CALL:
- return matcher.methodReferenceMatches((PsiMethodReferenceExpression)myContext);
- default:
- return false;
- }
+ @Nullable
+ @Override
+ public PsiExpression getExpression() {
+ return ObjectUtils.tryCast(myContext, PsiExpression.class);
}
/**
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/NotInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/NotInstruction.java
index 5cb17b0facee..090112be3243 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/NotInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/NotInstruction.java
@@ -16,10 +16,18 @@
package com.intellij.codeInspection.dataFlow.instructions;
-import com.intellij.codeInspection.dataFlow.*;
-import com.intellij.codeInspection.dataFlow.value.DfaValue;
+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.PsiPrefixExpression;
-public class NotInstruction extends Instruction {
+public class NotInstruction extends Instruction implements ExpressionPushingInstruction {
+ private final PsiPrefixExpression myAnchor;
+
+ public NotInstruction(PsiPrefixExpression anchor) {
+ myAnchor = anchor;
+ }
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
@@ -29,4 +37,9 @@ public class NotInstruction extends Instruction {
public String toString() {
return "NOT";
}
+
+ @Override
+ public PsiPrefixExpression getExpression() {
+ return myAnchor;
+ }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/PushInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/PushInstruction.java
index b282d9cb4c9f..2b0a0c559ed6 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/PushInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/PushInstruction.java
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-public class PushInstruction extends Instruction {
+public class PushInstruction extends Instruction implements ExpressionPushingInstruction {
private final DfaValue myValue;
private final PsiExpression myPlace;
private final boolean myReferenceWrite;
@@ -50,7 +50,8 @@ public class PushInstruction extends Instruction {
return myValue;
}
- public PsiExpression getPlace() {
+ @Override
+ public PsiExpression getExpression() {
return myPlace;
}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/Xor.java b/java/java-tests/testData/inspection/dataFlow/fixture/Xor.java
index 31b8f906a7cd..a13e5bf8048e 100644
--- a/java/java-tests/testData/inspection/dataFlow/fixture/Xor.java
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/Xor.java
@@ -4,7 +4,7 @@ class Some {
public static void main(String[] args) {
boolean x = true, y = true, z = true, t = true;
- boolean r = x ^ y ^ z ^ t;
+ boolean r = x ^ y ^ z ^ t;
System.out.println("r: " + r);
}