mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DFA instruction visitor refactoring wave#1
InstructionVisitor#beforeExpressionPush method which is called before every push of PsiExpression result (were it simple reference, operation or method call) CustomMethodHandlers: do not modify states; only return resulting value (current handlers do not produce more than one value) Xor polyadic now supports reporting for subexpressions
This commit is contained in:
+2
-2
@@ -555,11 +555,11 @@ public class GuessManagerImpl extends GuessManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
if (myForPlace == instruction.getPlace()) {
|
if (myForPlace == instruction.getExpression()) {
|
||||||
addToResult(((ExpressionTypeMemoryState)memState).getStates());
|
addToResult(((ExpressionTypeMemoryState)memState).getStates());
|
||||||
}
|
}
|
||||||
DfaInstructionState[] states = super.visitPush(instruction, runner, memState);
|
DfaInstructionState[] states = super.visitPush(instruction, runner, memState);
|
||||||
if (myForPlace == instruction.getPlace()) {
|
if (myForPlace == instruction.getExpression()) {
|
||||||
addConstraints(states);
|
addConstraints(states);
|
||||||
}
|
}
|
||||||
return states;
|
return states;
|
||||||
|
|||||||
@@ -125,6 +125,21 @@ public class CFGBuilder {
|
|||||||
return add(new PushInstruction(value, null));
|
return add(new PushInstruction(value, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate instructions to push given DfaValue on stack and bind it to given expression.
|
||||||
|
* <p>
|
||||||
|
* Stack before: ...
|
||||||
|
* <p>
|
||||||
|
* 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
|
* Generate instructions to pop single DfaValue from stack
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
+16
-76
@@ -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.
|
// 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;
|
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.DfaConstValue;
|
||||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||||
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.*;
|
import com.intellij.psi.*;
|
||||||
import com.intellij.psi.util.CachedValueProvider;
|
import com.intellij.psi.util.*;
|
||||||
import com.intellij.psi.util.CachedValuesManager;
|
|
||||||
import com.intellij.psi.util.PsiModificationTracker;
|
|
||||||
import com.intellij.psi.util.PsiUtil;
|
|
||||||
import com.intellij.util.JavaPsiConstructorUtil;
|
import com.intellij.util.JavaPsiConstructorUtil;
|
||||||
import com.intellij.util.ObjectUtils;
|
|
||||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
|
||||||
import one.util.streamex.StreamEx;
|
import one.util.streamex.StreamEx;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -42,6 +38,12 @@ public class CommonDataflow {
|
|||||||
newMap = newMap.with(DfaFactType.CAN_BE_NULL, false);
|
newMap = newMap.with(DfaFactType.CAN_BE_NULL, false);
|
||||||
}
|
}
|
||||||
myFacts.put(expression, existing == null ? newMap : existing.union(newMap));
|
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
|
@Override
|
||||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public void beforeExpressionPush(@NotNull DfaValue value,
|
||||||
DfaInstructionState[] states = super.visitPush(instruction, runner, memState);
|
@NotNull PsiExpression expression,
|
||||||
PsiExpression place = instruction.getPlace();
|
@Nullable TextRange range,
|
||||||
if (place != null && !instruction.isReferenceWrite()) {
|
@NotNull DfaMemoryState state) {
|
||||||
for (DfaInstructionState state : states) {
|
if (range == null && value != myFail) {
|
||||||
DfaMemoryState afterState = state.getMemoryState();
|
// Do not track instructions which cover part of expression
|
||||||
myResult.add(place, (DfaMemoryStateImpl)afterState, instruction.getValue());
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1454,8 +1454,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
|||||||
operand = operands[i];
|
operand = operands[i];
|
||||||
operand.accept(this);
|
operand.accept(this);
|
||||||
generateBoxingUnboxingInstructionFor(operand, exprType);
|
generateBoxingUnboxingInstructionFor(operand, exprType);
|
||||||
PsiElement psiAnchor = i == operands.length - 1 && expression.isPhysical() ? expression : null;
|
PsiExpression psiAnchor = expression.isPhysical() ? expression : null;
|
||||||
addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, exprType));
|
addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, exprType, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1932,7 +1932,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
|||||||
addInstruction(new AssignInstruction(operand, null, myFactory.createValue(operand)));
|
addInstruction(new AssignInstruction(operand, null, myFactory.createValue(operand)));
|
||||||
}
|
}
|
||||||
else if (expression.getOperationTokenType() == JavaTokenType.EXCL) {
|
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))) {
|
else if (expression.getOperationTokenType() == JavaTokenType.MINUS && (PsiType.INT.equals(type) || PsiType.LONG.equals(type))) {
|
||||||
addInstruction(new PushInstruction(myFactory.getConstFactory().createDefault(type), null));
|
addInstruction(new PushInstruction(myFactory.getConstFactory().createDefault(type), null));
|
||||||
|
|||||||
+28
-23
@@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.intellij.psi.CommonClassNames.*;
|
import static com.intellij.psi.CommonClassNames.*;
|
||||||
@@ -53,12 +52,14 @@ class CustomMethodHandlers {
|
|||||||
|
|
||||||
interface CustomMethodHandler {
|
interface CustomMethodHandler {
|
||||||
|
|
||||||
List<DfaMemoryState> handle(DfaCallArguments callArguments, DfaMemoryState memState, DfaValueFactory factory);
|
@Nullable
|
||||||
|
DfaValue getMethodResult(DfaCallArguments callArguments, DfaMemoryState memState, DfaValueFactory factory);
|
||||||
|
|
||||||
default CustomMethodHandler compose(CustomMethodHandler other) {
|
default CustomMethodHandler compose(CustomMethodHandler other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return (args, memState, factory) -> {
|
return (args, memState, factory) -> {
|
||||||
List<DfaMemoryState> result = this.handle(args, memState, factory);
|
DfaValue result = this.getMethodResult(args, memState, factory);
|
||||||
return result.isEmpty() ? other.handle(args, memState, factory) : result;
|
return result == null ? other.getMethodResult(args, memState, factory) : result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,16 +72,15 @@ class CustomMethodHandlers {
|
|||||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
|
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
|
||||||
(args, memState, factory) -> mathAbs(args.myArguments, memState, factory, false))
|
(args, memState, factory) -> mathAbs(args.myArguments, memState, factory, false))
|
||||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("long"),
|
.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) {
|
public static CustomMethodHandler find(MethodCallInstruction instruction) {
|
||||||
PsiMethod method = instruction.getTargetMethod();
|
PsiMethod method = instruction.getTargetMethod();
|
||||||
CustomMethodHandler handler = null;
|
CustomMethodHandler handler = null;
|
||||||
if (isConstantCall(method)) {
|
if (isConstantCall(method)) {
|
||||||
handler = (args, memState, factory) -> {
|
handler = (args, memState, factory) -> handleConstantCall(args, memState, factory, method);
|
||||||
DfaValue value = handleConstantCall(args, memState, factory, method);
|
|
||||||
return value == null ? Collections.emptyList() : singleResult(memState, value);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
CustomMethodHandler handler2 = CUSTOM_METHOD_HANDLERS.mapFirst(method);
|
CustomMethodHandler handler2 = CUSTOM_METHOD_HANDLERS.mapFirst(method);
|
||||||
return handler == null ? handler2 : handler.compose(handler2);
|
return handler == null ? handler2 : handler.compose(handler2);
|
||||||
@@ -190,27 +190,32 @@ class CustomMethodHandlers {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<DfaMemoryState> indexOf(DfaValue qualifier,
|
private static DfaValue indexOf(DfaValue qualifier,
|
||||||
DfaMemoryState memState,
|
DfaMemoryState memState,
|
||||||
DfaValueFactory factory,
|
DfaValueFactory factory,
|
||||||
SpecialField specialField) {
|
SpecialField specialField) {
|
||||||
DfaValue length = specialField.createValue(factory, qualifier);
|
DfaValue length = specialField.createValue(factory, qualifier);
|
||||||
LongRangeSet range = memState.getValueFact(length, DfaFactType.RANGE);
|
LongRangeSet range = memState.getValueFact(length, DfaFactType.RANGE);
|
||||||
long maxLen = range == null || range.isEmpty() ? Integer.MAX_VALUE : range.max();
|
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<DfaMemoryState> 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);
|
DfaValue arg = ArrayUtil.getFirstElement(args);
|
||||||
if(arg == null) return Collections.emptyList();
|
if (arg == null) return null;
|
||||||
LongRangeSet range = memState.getValueFact(arg, DfaFactType.RANGE);
|
LongRangeSet range = memState.getValueFact(arg, DfaFactType.RANGE);
|
||||||
if (range == null) return Collections.emptyList();
|
if (range == null) return null;
|
||||||
return singleResult(memState, factory.getFactValue(DfaFactType.RANGE, range.abs(isLong)));
|
return factory.getFactValue(DfaFactType.RANGE, range.abs(isLong));
|
||||||
}
|
|
||||||
|
|
||||||
private static List<DfaMemoryState> singleResult(DfaMemoryState state, DfaValue value) {
|
|
||||||
state.push(value);
|
|
||||||
return Collections.singletonList(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object getConstantValue(DfaMemoryState memoryState, DfaValue value) {
|
private static Object getConstantValue(DfaMemoryState memoryState, DfaValue value) {
|
||||||
|
|||||||
+24
-28
@@ -477,7 +477,7 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
|||||||
Set<PsiElement> reportedAnchors) {
|
Set<PsiElement> reportedAnchors) {
|
||||||
for (Instruction instruction : runner.getInstructions()) {
|
for (Instruction instruction : runner.getInstructions()) {
|
||||||
if (instruction instanceof PushInstruction) {
|
if (instruction instanceof PushInstruction) {
|
||||||
PsiExpression place = ((PushInstruction)instruction).getPlace();
|
PsiExpression place = ((PushInstruction)instruction).getExpression();
|
||||||
DfaValue value = ((PushInstruction)instruction).getValue();
|
DfaValue value = ((PushInstruction)instruction).getValue();
|
||||||
Object constant = value instanceof DfaConstValue ? ((DfaConstValue)value).getValue() : null;
|
Object constant = value instanceof DfaConstValue ? ((DfaConstValue)value).getValue() : null;
|
||||||
if (place instanceof PsiPolyadicExpression && constant instanceof Boolean && !isFlagCheck(place) && reportedAnchors.add(place)) {
|
if (place instanceof PsiPolyadicExpression && constant instanceof Boolean && !isFlagCheck(place) && reportedAnchors.add(place)) {
|
||||||
@@ -489,20 +489,16 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
|||||||
|
|
||||||
private static void reportOptionalOfNullableImprovements(ProblemsHolder holder,
|
private static void reportOptionalOfNullableImprovements(ProblemsHolder holder,
|
||||||
Set<PsiElement> reportedAnchors,
|
Set<PsiElement> reportedAnchors,
|
||||||
Map<MethodCallInstruction, ThreeState> nullArgs) {
|
Map<PsiElement, ThreeState> nullArgs) {
|
||||||
nullArgs.forEach((call, nullArg) -> {
|
nullArgs.forEach((anchor, alwaysPresent) -> {
|
||||||
PsiElement arg = call.getArgumentAnchor(0);
|
if (alwaysPresent == ThreeState.UNSURE) return;
|
||||||
if (reportedAnchors.add(arg)) {
|
if (reportedAnchors.add(anchor)) {
|
||||||
switch (nullArg) {
|
if (alwaysPresent.toBoolean()) {
|
||||||
case YES:
|
holder.registerProblem(anchor, "Passing a non-null argument to <code>Optional</code>",
|
||||||
holder.registerProblem(arg, "Passing <code>null</code> argument to <code>Optional</code>",
|
DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix(anchor));
|
||||||
DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(arg));
|
} else {
|
||||||
break;
|
holder.registerProblem(anchor, "Passing <code>null</code> argument to <code>Optional</code>",
|
||||||
case NO:
|
DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(anchor));
|
||||||
holder.registerProblem(arg, "Passing a non-null argument to <code>Optional</code>",
|
|
||||||
DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix(arg));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -653,10 +649,12 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
|||||||
InspectionsBundle.message("dataflow.message.unreachable.switch.label"));
|
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);
|
boolean evaluatesToTrue = trueSet.contains(instruction);
|
||||||
final PsiElement parent = psiAnchor.getParent();
|
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(
|
holder.registerProblem(
|
||||||
psiAnchor,
|
psiAnchor,
|
||||||
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
|
InspectionsBundle.message("dataflow.message.pointless.assignment.expression", Boolean.toString(evaluatesToTrue)),
|
||||||
@@ -664,19 +662,17 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (instruction instanceof BinopInstruction) {
|
TextRange range =
|
||||||
TextRange range = ((BinopInstruction)instruction).getAnchorRange();
|
instruction instanceof ExpressionPushingInstruction ? ((ExpressionPushingInstruction)instruction).getExpressionRange() : null;
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
// report rare cases like a == b == c where "a == b" part is constant
|
// report rare cases like a == b == c where "a == b" part is constant
|
||||||
String message = InspectionsBundle.message("dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
String message = InspectionsBundle.message("dataflow.message.constant.condition", Boolean.toString(evaluatesToTrue));
|
||||||
holder.registerProblem(psiAnchor, range, message);
|
holder.registerProblem(psiAnchor, range, message);
|
||||||
// do not add to reported anchors if only part of expression was reported
|
// do not add to reported anchors if only part of expression was reported
|
||||||
return;
|
} else if (reportedAnchors.add(psiAnchor)) {
|
||||||
}
|
reportConstantCondition(holder, psiAnchor, evaluatesToTrue);
|
||||||
}
|
}
|
||||||
reportConstantCondition(holder, psiAnchor, evaluatesToTrue);
|
|
||||||
}
|
}
|
||||||
reportedAnchors.add(psiAnchor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-11
@@ -5,6 +5,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*;
|
|||||||
import com.intellij.codeInspection.dataFlow.value.*;
|
import com.intellij.codeInspection.dataFlow.value.*;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.*;
|
import com.intellij.psi.*;
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import com.intellij.psi.util.PsiTypesUtil;
|
import com.intellij.psi.util.PsiTypesUtil;
|
||||||
@@ -27,7 +28,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
|||||||
private final Set<Instruction> myCCEInstructions = ContainerUtil.newHashSet();
|
private final Set<Instruction> myCCEInstructions = ContainerUtil.newHashSet();
|
||||||
private final Map<MethodCallInstruction, Boolean> myFailingCalls = new HashMap<>();
|
private final Map<MethodCallInstruction, Boolean> myFailingCalls = new HashMap<>();
|
||||||
private final Map<PsiMethodCallExpression, ThreeState> myBooleanCalls = new HashMap<>();
|
private final Map<PsiMethodCallExpression, ThreeState> myBooleanCalls = new HashMap<>();
|
||||||
private final Map<MethodCallInstruction, ThreeState> myOfNullableCalls = new HashMap<>();
|
private final Map<PsiElement, ThreeState> myOfNullableCalls = new HashMap<>();
|
||||||
private final Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> myArrayStoreProblems = new HashMap<>();
|
private final Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> myArrayStoreProblems = new HashMap<>();
|
||||||
private final Map<PsiMethodReferenceExpression, DfaValue> myMethodReferenceResults = new HashMap<>();
|
private final Map<PsiMethodReferenceExpression, DfaValue> myMethodReferenceResults = new HashMap<>();
|
||||||
private final Map<PsiArrayAccessExpression, ThreeState> myOutOfBoundsArrayAccesses = new HashMap<>();
|
private final Map<PsiArrayAccessExpression, ThreeState> myOutOfBoundsArrayAccesses = new HashMap<>();
|
||||||
@@ -123,7 +124,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
|||||||
return myArrayStoreProblems;
|
return myArrayStoreProblems;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<MethodCallInstruction, ThreeState> getOfNullableCalls() {
|
Map<PsiElement, ThreeState> getOfNullableCalls() {
|
||||||
return myOfNullableCalls;
|
return myOfNullableCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,17 +162,37 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
|||||||
ContainerUtil.exists(instructions, i -> i instanceof ReturnInstruction && ((ReturnInstruction)i).getAnchor() instanceof PsiReturnStatement);
|
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
|
@Override
|
||||||
public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction,
|
public DfaInstructionState[] visitMethodCall(MethodCallInstruction instruction,
|
||||||
DataFlowRunner runner,
|
DataFlowRunner runner,
|
||||||
DfaMemoryState memState) {
|
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.CAN_BE_NULL, true);
|
|
||||||
myOfNullableCalls.merge(instruction, nullArg, ThreeState::merge);
|
|
||||||
}
|
|
||||||
DfaInstructionState[] states = super.visitMethodCall(instruction, runner, memState);
|
DfaInstructionState[] states = super.visitMethodCall(instruction, runner, memState);
|
||||||
if (hasNonTrivialFailingContracts(instruction)) {
|
if (hasNonTrivialFailingContracts(instruction)) {
|
||||||
DfaConstValue fail = runner.getFactory().getConstFactory().getContractFail();
|
DfaConstValue fail = runner.getFactory().getConstFactory().getContractFail();
|
||||||
@@ -226,7 +247,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
PsiExpression place = instruction.getPlace();
|
PsiExpression place = instruction.getExpression();
|
||||||
if (!instruction.isReferenceWrite() && place instanceof PsiReferenceExpression) {
|
if (!instruction.isReferenceWrite() && place instanceof PsiReferenceExpression) {
|
||||||
DfaValue dfaValue = instruction.getValue();
|
DfaValue dfaValue = instruction.getValue();
|
||||||
if (dfaValue instanceof DfaVariableValue) {
|
if (dfaValue instanceof DfaVariableValue) {
|
||||||
@@ -253,7 +274,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
|||||||
if (values.size() == 1) {
|
if (values.size() == 1) {
|
||||||
Object singleValue = values.iterator().next();
|
Object singleValue = values.iterator().next();
|
||||||
if (singleValue != ANY_VALUE) {
|
if (singleValue != ANY_VALUE) {
|
||||||
result.add(Pair.create((PsiReferenceExpression)instruction.getPlace(), (DfaConstValue)singleValue));
|
result.add(Pair.create((PsiReferenceExpression)instruction.getExpression(), (DfaConstValue)singleValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import com.intellij.psi.util.CachedValueProvider;
|
|||||||
import com.intellij.psi.util.CachedValuesManager;
|
import com.intellij.psi.util.CachedValuesManager;
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import com.intellij.psi.util.PsiUtil;
|
import com.intellij.psi.util.PsiUtil;
|
||||||
import com.intellij.ui.treeStructure.NullNode;
|
|
||||||
import com.intellij.util.IncorrectOperationException;
|
import com.intellij.util.IncorrectOperationException;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import com.intellij.util.containers.FList;
|
import com.intellij.util.containers.FList;
|
||||||
@@ -381,7 +380,7 @@ public class DfaUtil {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
PsiExpression place = instruction.getPlace();
|
PsiExpression place = instruction.getExpression();
|
||||||
if (place != null) {
|
if (place != null) {
|
||||||
PlaceResult result = myResults.computeIfAbsent(place, __ -> new PlaceResult());
|
PlaceResult result = myResults.computeIfAbsent(place, __ -> new PlaceResult());
|
||||||
((ValuableDataFlowRunner.MyDfaMemoryState)memState).forVariableStates((variableValue, value) -> {
|
((ValuableDataFlowRunner.MyDfaMemoryState)memState).forVariableStates((variableValue, value) -> {
|
||||||
|
|||||||
+24
-5
@@ -18,12 +18,14 @@ package com.intellij.codeInspection.dataFlow;
|
|||||||
import com.intellij.codeInsight.Nullability;
|
import com.intellij.codeInsight.Nullability;
|
||||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||||
import com.intellij.codeInspection.dataFlow.value.*;
|
import com.intellij.codeInspection.dataFlow.value.*;
|
||||||
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.PsiArrayAccessExpression;
|
import com.intellij.psi.PsiArrayAccessExpression;
|
||||||
import com.intellij.psi.PsiExpression;
|
import com.intellij.psi.PsiExpression;
|
||||||
import com.intellij.psi.PsiType;
|
import com.intellij.psi.PsiType;
|
||||||
import com.intellij.psi.util.PsiUtil;
|
import com.intellij.psi.util.PsiUtil;
|
||||||
import com.intellij.util.ObjectUtils;
|
import com.intellij.util.ObjectUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -32,6 +34,23 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public abstract class InstructionVisitor {
|
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) {
|
public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
memState.pop();
|
memState.pop();
|
||||||
DfaValue dest = memState.pop();
|
DfaValue dest = memState.pop();
|
||||||
@@ -89,7 +108,7 @@ public abstract class InstructionVisitor {
|
|||||||
public DfaInstructionState[] visitBinop(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitBinop(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
memState.pop();
|
memState.pop();
|
||||||
memState.pop();
|
memState.pop();
|
||||||
memState.push(DfaUnknownValue.getInstance());
|
pushExpressionResult(DfaUnknownValue.getInstance(), instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +202,7 @@ public abstract class InstructionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memState.pop(); //qualifier
|
memState.pop(); //qualifier
|
||||||
memState.push(DfaUnknownValue.getInstance());
|
pushExpressionResult(DfaUnknownValue.getInstance(), instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,19 +214,19 @@ public abstract class InstructionVisitor {
|
|||||||
DfaValue dfaValue = memState.pop();
|
DfaValue dfaValue = memState.pop();
|
||||||
|
|
||||||
dfaValue = dfaValue.createNegated();
|
dfaValue = dfaValue.createNegated();
|
||||||
memState.push(dfaValue);
|
pushExpressionResult(dfaValue, instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
memState.push(instruction.getValue());
|
pushExpressionResult(instruction.getValue(), instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DfaInstructionState[] visitArrayAccess(ArrayAccessInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
public DfaInstructionState[] visitArrayAccess(ArrayAccessInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||||
memState.pop(); // index
|
memState.pop(); // index
|
||||||
memState.pop(); // array reference
|
memState.pop(); // array reference
|
||||||
memState.push(instruction.getValue());
|
pushExpressionResult(instruction.getValue(), instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+60
-46
@@ -195,7 +195,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
if (arrayElementValue != DfaUnknownValue.getInstance()) {
|
if (arrayElementValue != DfaUnknownValue.getInstance()) {
|
||||||
result = arrayElementValue;
|
result = arrayElementValue;
|
||||||
}
|
}
|
||||||
memState.push(result);
|
pushExpressionResult(result, instruction, memState);
|
||||||
return nextInstruction(instruction, runner, memState);
|
return nextInstruction(instruction, runner, memState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
if (contracts.isEmpty()) return;
|
if (contracts.isEmpty()) return;
|
||||||
PsiType returnType = substitutor.substitute(method.getReturnType());
|
PsiType returnType = substitutor.substitute(method.getReturnType());
|
||||||
DfaValue defaultResult = runner.getFactory().createTypeValue(returnType, DfaPsiUtil.getElementNullability(returnType, method));
|
DfaValue defaultResult = runner.getFactory().createTypeValue(returnType, DfaPsiUtil.getElementNullability(returnType, method));
|
||||||
Stream<DfaValue> returnValues = possibleReturnValues(callArguments, state, contracts, runner.getFactory(), defaultResult);
|
Stream<DfaValue> returnValues = possibleReturnValues(callArguments, state, contracts, runner.getFactory(), defaultResult, methodRef);
|
||||||
returnValues.forEach(res -> processMethodReferenceResult(methodRef, contracts, res));
|
returnValues.forEach(res -> processMethodReferenceResult(methodRef, contracts, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,14 +268,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
return new DfaCallArguments(qualifier, arguments, JavaMethodContractUtil.isPure(method));
|
return new DfaCallArguments(qualifier, arguments, JavaMethodContractUtil.isPure(method));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<DfaValue> possibleReturnValues(DfaCallArguments callArguments,
|
private Stream<DfaValue> possibleReturnValues(DfaCallArguments callArguments,
|
||||||
DfaMemoryState state,
|
DfaMemoryState state,
|
||||||
List<? extends MethodContract> contracts,
|
List<? extends MethodContract> contracts,
|
||||||
DfaValueFactory factory, DfaValue defaultResult) {
|
DfaValueFactory factory,
|
||||||
|
DfaValue defaultResult,
|
||||||
|
PsiMethodReferenceExpression methodRef) {
|
||||||
Set<DfaCallState> currentStates = Collections.singleton(new DfaCallState(state.createClosureState(), callArguments));
|
Set<DfaCallState> currentStates = Collections.singleton(new DfaCallState(state.createClosureState(), callArguments));
|
||||||
Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet();
|
Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet();
|
||||||
for (MethodContract contract : contracts) {
|
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)
|
return StreamEx.of(finalStates).map(DfaMemoryState::peek)
|
||||||
.append(currentStates.isEmpty() ? StreamEx.empty() : StreamEx.of(defaultResult)).distinct();
|
.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());
|
DfaValue defaultResult = getMethodResultValue(instruction, callArguments.myQualifier, memState, runner.getFactory());
|
||||||
if (callArguments.myArguments != null) {
|
if (callArguments.myArguments != null) {
|
||||||
for (MethodContract contract : instruction.getContracts()) {
|
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 (currentStates.size() + finalStates.size() > DataFlowRunner.MAX_STATES_PER_BRANCH) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Too complex contract on " + instruction.getContext() + ", skipping contract processing");
|
LOG.debug("Too complex contract on " + instruction.getContext() + ", skipping contract processing");
|
||||||
@@ -328,7 +330,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (DfaCallState callState : currentStates) {
|
for (DfaCallState callState : currentStates) {
|
||||||
callState.myMemoryState.push(defaultResult);
|
pushExpressionResult(defaultResult, instruction, callState.myMemoryState);
|
||||||
finalStates.add(callState.myMemoryState);
|
finalStates.add(callState.myMemoryState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,8 +358,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
if (handler == null) return Collections.emptyList();
|
if (handler == null) return Collections.emptyList();
|
||||||
memState = memState.createCopy();
|
memState = memState.createCopy();
|
||||||
DfaCallArguments callArguments = popCall(instruction, runner, memState, false);
|
DfaCallArguments callArguments = popCall(instruction, runner, memState, false);
|
||||||
return callArguments.myArguments == null ? Collections.emptyList() :
|
DfaValue result = callArguments.myArguments == null ? null : handler.getMethodResult(callArguments, memState, runner.getFactory());
|
||||||
handler.handle(callArguments, memState, runner.getFactory());
|
if (result != null) {
|
||||||
|
pushExpressionResult(result, instruction, memState);
|
||||||
|
return Collections.singletonList(memState);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -447,15 +453,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<DfaCallState> addContractResults(MethodContract contract,
|
private Set<DfaCallState> addContractResults(MethodContract contract,
|
||||||
Set<DfaCallState> states,
|
Set<DfaCallState> states,
|
||||||
DfaValueFactory factory,
|
DfaValueFactory factory,
|
||||||
Set<DfaMemoryState> finalStates,
|
Set<DfaMemoryState> finalStates,
|
||||||
DfaValue defaultResult) {
|
DfaValue defaultResult,
|
||||||
|
PsiExpression expression) {
|
||||||
if(contract.isTrivial()) {
|
if(contract.isTrivial()) {
|
||||||
for (DfaCallState callState : states) {
|
for (DfaCallState callState : states) {
|
||||||
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, callState);
|
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, callState);
|
||||||
callState.myMemoryState.push(result);
|
pushExpressionResult(result, () -> expression, callState.myMemoryState);
|
||||||
finalStates.add(callState.myMemoryState);
|
finalStates.add(callState.myMemoryState);
|
||||||
}
|
}
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@@ -484,7 +491,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
}
|
}
|
||||||
if(state != null) {
|
if(state != null) {
|
||||||
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, new DfaCallState(state, arguments));
|
DfaValue result = contract.getReturnValue().getDfaValue(factory, defaultResult, new DfaCallState(state, arguments));
|
||||||
state.push(result);
|
pushExpressionResult(result, () -> expression, state);
|
||||||
finalStates.add(state);
|
finalStates.add(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,7 +637,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DfaValue result = null;
|
DfaValue result = DfaUnknownValue.getInstance();
|
||||||
PsiType type = instruction.getResultType();
|
PsiType type = instruction.getResultType();
|
||||||
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
||||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||||
@@ -642,10 +649,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);
|
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.setTrueReachable(); // Not a branching instruction actually.
|
||||||
instruction.setFalseReachable();
|
instruction.setFalseReachable();
|
||||||
@@ -654,12 +661,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static DfaInstructionState[] handleRelationBinop(BinopInstruction instruction,
|
private DfaInstructionState[] handleRelationBinop(BinopInstruction instruction,
|
||||||
DataFlowRunner runner,
|
DataFlowRunner runner,
|
||||||
DfaMemoryState memState,
|
DfaMemoryState memState,
|
||||||
DfaValue dfaRight,
|
DfaValue dfaRight,
|
||||||
DfaValue dfaLeft,
|
DfaValue dfaLeft,
|
||||||
RelationType relationType) {
|
RelationType relationType) {
|
||||||
DfaValueFactory factory = runner.getFactory();
|
DfaValueFactory factory = runner.getFactory();
|
||||||
RelationType[] relations = splitRelation(relationType);
|
RelationType[] relations = splitRelation(relationType);
|
||||||
|
|
||||||
@@ -767,11 +774,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static DfaInstructionState[] handleConstantComparison(BinopInstruction instruction,
|
private DfaInstructionState[] handleConstantComparison(BinopInstruction instruction,
|
||||||
DataFlowRunner runner,
|
DataFlowRunner runner,
|
||||||
DfaMemoryState memState,
|
DfaMemoryState memState,
|
||||||
DfaValue dfaRight,
|
DfaValue dfaRight,
|
||||||
DfaValue dfaLeft, RelationType relationType) {
|
DfaValue dfaLeft, RelationType relationType) {
|
||||||
if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaVariableValue) {
|
if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaVariableValue) {
|
||||||
Number leftValue = getKnownNumberValue(memState, (DfaVariableValue)dfaLeft);
|
Number leftValue = getKnownNumberValue(memState, (DfaVariableValue)dfaLeft);
|
||||||
Number rightValue = getKnownNumberValue(memState, (DfaVariableValue)dfaRight);
|
Number rightValue = getKnownNumberValue(memState, (DfaVariableValue)dfaRight);
|
||||||
@@ -810,11 +817,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static DfaInstructionState[] checkComparingWithConstant(BinopInstruction instruction,
|
private DfaInstructionState[] checkComparingWithConstant(BinopInstruction instruction,
|
||||||
DataFlowRunner runner,
|
DataFlowRunner runner,
|
||||||
DfaMemoryState memState,
|
DfaMemoryState memState,
|
||||||
DfaVariableValue var,
|
DfaVariableValue var,
|
||||||
RelationType opSign, Number comparedWith) {
|
RelationType opSign, Number comparedWith) {
|
||||||
Number knownValue = getKnownNumberValue(memState, var);
|
Number knownValue = getKnownNumberValue(memState, var);
|
||||||
if (knownValue != null) {
|
if (knownValue != null) {
|
||||||
return checkComparisonWithKnownValue(instruction, runner, memState, opSign, knownValue, comparedWith);
|
return checkComparisonWithKnownValue(instruction, runner, memState, opSign, knownValue, comparedWith);
|
||||||
@@ -828,12 +835,12 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
return knownConstantValue != null && knownConstantValue.getValue() instanceof Number ? (Number)knownConstantValue.getValue() : null;
|
return knownConstantValue != null && knownConstantValue.getValue() instanceof Number ? (Number)knownConstantValue.getValue() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DfaInstructionState[] checkComparisonWithKnownValue(BinopInstruction instruction,
|
private DfaInstructionState[] checkComparisonWithKnownValue(BinopInstruction instruction,
|
||||||
DataFlowRunner runner,
|
DataFlowRunner runner,
|
||||||
DfaMemoryState memState,
|
DfaMemoryState memState,
|
||||||
RelationType opSign,
|
RelationType opSign,
|
||||||
Number leftValue,
|
Number leftValue,
|
||||||
Number rightValue) {
|
Number rightValue) {
|
||||||
int cmp = compare(leftValue, rightValue);
|
int cmp = compare(leftValue, rightValue);
|
||||||
Boolean result = null;
|
Boolean result = null;
|
||||||
boolean hasNaN = DfaUtil.isNaN(leftValue) || DfaUtil.isNaN(rightValue);
|
boolean hasNaN = DfaUtil.isNaN(leftValue) || DfaUtil.isNaN(rightValue);
|
||||||
@@ -867,12 +874,19 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
|||||||
return Double.compare(a.doubleValue(), b.doubleValue());
|
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))};
|
return new DfaInstructionState[]{makeBooleanResult(instruction, runner, memState, ThreeState.fromBoolean(result))};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DfaInstructionState makeBooleanResult(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState, @NotNull ThreeState result) {
|
private DfaInstructionState makeBooleanResult(BinopInstruction instruction,
|
||||||
memState.push(result == ThreeState.UNSURE ? DfaUnknownValue.getInstance() : runner.getFactory().getBoolean(result.toBoolean()));
|
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) {
|
if (result != ThreeState.NO) {
|
||||||
instruction.setTrueReachable();
|
instruction.setTrueReachable();
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-7
@@ -256,14 +256,23 @@ public class OptionalChainInliner implements CallInliner {
|
|||||||
|
|
||||||
private static void inlineOf(CFGBuilder builder, PsiType optionalElementType, PsiMethodCallExpression qualifierCall) {
|
private static void inlineOf(CFGBuilder builder, PsiType optionalElementType, PsiMethodCallExpression qualifierCall) {
|
||||||
PsiExpression argument = qualifierCall.getArgumentList().getExpressions()[0];
|
PsiExpression argument = qualifierCall.getArgumentList().getExpressions()[0];
|
||||||
builder.pushExpression(argument)
|
builder
|
||||||
.boxUnbox(argument, optionalElementType)
|
.pushExpression(argument)
|
||||||
.pushUnknown() // ... arg, ?
|
.boxUnbox(argument, optionalElementType);
|
||||||
.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
|
|
||||||
if ("of".equals(qualifierCall.getMethodExpression().getReferenceName())) {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-4
@@ -163,8 +163,10 @@ public class StreamChainInliner implements CallInliner {
|
|||||||
myNext.pushResult(builder);
|
myNext.pushResult(builder);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
builder.push(builder.getFactory()
|
DfaValue resultValue =
|
||||||
.createTypeValue(myCall.getType(), DfaPsiUtil.getElementNullability(myCall.getType(), myCall.resolveMethod())));
|
builder.getFactory().createTypeValue(myCall.getType(),
|
||||||
|
DfaPsiUtil.getElementNullability(myCall.getType(), myCall.resolveMethod()));
|
||||||
|
builder.push(resultValue, myCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +216,7 @@ public class StreamChainInliner implements CallInliner {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void pushResult(CFGBuilder builder) {
|
void pushResult(CFGBuilder builder) {
|
||||||
builder.push(myResult);
|
builder.push(myResult, myCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,7 +632,7 @@ public class StreamChainInliner implements CallInliner {
|
|||||||
.ifConditionIs(true)
|
.ifConditionIs(true)
|
||||||
.chain(b -> buildStreamCFG(b, firstStep, originalQualifier))
|
.chain(b -> buildStreamCFG(b, firstStep, originalQualifier))
|
||||||
.end()
|
.end()
|
||||||
.push(builder.getFactory().createTypeValue(call.getType(), Nullability.NOT_NULL));
|
.push(builder.getFactory().createTypeValue(call.getType(), Nullability.NOT_NULL), call);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
|||||||
import com.intellij.psi.PsiArrayAccessExpression;
|
import com.intellij.psi.PsiArrayAccessExpression;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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 DfaValue myValue;
|
||||||
private final @NotNull PsiArrayAccessExpression myExpression;
|
private final @NotNull PsiArrayAccessExpression myExpression;
|
||||||
|
|
||||||
|
|||||||
+10
-5
@@ -21,7 +21,6 @@ import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
|||||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||||
import com.intellij.openapi.util.TextRange;
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.PsiExpression;
|
import com.intellij.psi.PsiExpression;
|
||||||
import com.intellij.psi.PsiPolyadicExpression;
|
import com.intellij.psi.PsiPolyadicExpression;
|
||||||
import com.intellij.psi.PsiType;
|
import com.intellij.psi.PsiType;
|
||||||
@@ -31,18 +30,18 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import static com.intellij.psi.JavaTokenType.*;
|
import static com.intellij.psi.JavaTokenType.*;
|
||||||
|
|
||||||
public class BinopInstruction extends BranchingInstruction {
|
public class BinopInstruction extends BranchingInstruction implements ExpressionPushingInstruction {
|
||||||
private static final TokenSet ourSignificantOperations =
|
private static final TokenSet ourSignificantOperations =
|
||||||
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, GTGT, GTGTGT);
|
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, GTGT, GTGTGT);
|
||||||
private final IElementType myOperationSign;
|
private final IElementType myOperationSign;
|
||||||
private final @Nullable PsiType myResultType;
|
private final @Nullable PsiType myResultType;
|
||||||
private final int myLastOperand;
|
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);
|
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);
|
super(psiAnchor);
|
||||||
myResultType = resultType;
|
myResultType = resultType;
|
||||||
myOperationSign = ourSignificantOperations.contains(opSign) ? opSign : null;
|
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
|
* @return range inside the anchor which evaluates this instruction, or null if the whole anchor evaluates this instruction
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public TextRange getAnchorRange() {
|
public TextRange getExpressionRange() {
|
||||||
if (myLastOperand != -1 && getPsiAnchor() instanceof PsiPolyadicExpression) {
|
if (myLastOperand != -1 && getPsiAnchor() instanceof PsiPolyadicExpression) {
|
||||||
PsiPolyadicExpression anchor = (PsiPolyadicExpression)getPsiAnchor();
|
PsiPolyadicExpression anchor = (PsiPolyadicExpression)getPsiAnchor();
|
||||||
PsiExpression[] operands = anchor.getOperands();
|
PsiExpression[] operands = anchor.getOperands();
|
||||||
@@ -64,6 +63,12 @@ public class BinopInstruction extends BranchingInstruction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public PsiExpression getExpression() {
|
||||||
|
return (PsiExpression)getPsiAnchor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
||||||
return visitor.visitBinop(this, runner, stateBefore);
|
return visitor.visitBinop(this, runner, stateBefore);
|
||||||
|
|||||||
+25
@@ -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;}
|
||||||
|
}
|
||||||
+5
-2
@@ -19,7 +19,10 @@ import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
|||||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ public class InstanceofInstruction extends BinopInstruction {
|
|||||||
@Nullable private final PsiExpression myLeft;
|
@Nullable private final PsiExpression myLeft;
|
||||||
@Nullable private final PsiType myCastType;
|
@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);
|
super(JavaTokenType.INSTANCEOF_KEYWORD, psiAnchor, PsiType.BOOLEAN);
|
||||||
myLeft = left;
|
myLeft = left;
|
||||||
myCastType = castType;
|
myCastType = castType;
|
||||||
|
|||||||
+5
-11
@@ -21,14 +21,13 @@ import com.intellij.codeInspection.dataFlow.*;
|
|||||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||||
import com.intellij.psi.*;
|
import com.intellij.psi.*;
|
||||||
import com.intellij.util.ObjectUtils;
|
import com.intellij.util.ObjectUtils;
|
||||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
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];
|
private static final Nullability[] EMPTY_NULLABILITY_ARRAY = new Nullability[0];
|
||||||
|
|
||||||
@Nullable private final PsiType myType;
|
@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);
|
myReturnNullability = call instanceof PsiNewExpression ? Nullability.NOT_NULL : DfaPsiUtil.getElementNullability(myType, myTargetMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(CallMatcher matcher) {
|
@Nullable
|
||||||
switch (myMethodType) {
|
@Override
|
||||||
case REGULAR_METHOD_CALL:
|
public PsiExpression getExpression() {
|
||||||
return myContext instanceof PsiMethodCallExpression && matcher.test((PsiMethodCallExpression)myContext);
|
return ObjectUtils.tryCast(myContext, PsiExpression.class);
|
||||||
case METHOD_REFERENCE_CALL:
|
|
||||||
return matcher.methodReferenceMatches((PsiMethodReferenceExpression)myContext);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+16
-3
@@ -16,10 +16,18 @@
|
|||||||
|
|
||||||
package com.intellij.codeInspection.dataFlow.instructions;
|
package com.intellij.codeInspection.dataFlow.instructions;
|
||||||
|
|
||||||
import com.intellij.codeInspection.dataFlow.*;
|
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
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
|
@Override
|
||||||
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
||||||
@@ -29,4 +37,9 @@ public class NotInstruction extends Instruction {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "NOT";
|
return "NOT";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PsiPrefixExpression getExpression() {
|
||||||
|
return myAnchor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiExpression;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class PushInstruction extends Instruction {
|
public class PushInstruction extends Instruction implements ExpressionPushingInstruction {
|
||||||
private final DfaValue myValue;
|
private final DfaValue myValue;
|
||||||
private final PsiExpression myPlace;
|
private final PsiExpression myPlace;
|
||||||
private final boolean myReferenceWrite;
|
private final boolean myReferenceWrite;
|
||||||
@@ -50,7 +50,8 @@ public class PushInstruction extends Instruction {
|
|||||||
return myValue;
|
return myValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PsiExpression getPlace() {
|
@Override
|
||||||
|
public PsiExpression getExpression() {
|
||||||
return myPlace;
|
return myPlace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class Some {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
boolean x = true, y = true, z = true, t = true;
|
boolean x = true, y = true, z = true, t = true;
|
||||||
boolean r = <warning descr="Condition 'x ^ y ^ z ^ t' is always 'false'">x ^ y ^ z ^ t</warning>;
|
boolean r = <warning descr="Condition 'x ^ y ^ z ^ t' is always 'false'"><warning descr="Condition 'x ^ y ^ z' is always 'true'"><warning descr="Condition 'x ^ y' is always 'false'">x ^ y</warning> ^ z</warning> ^ t</warning>;
|
||||||
System.out.println("r: " + r);
|
System.out.println("r: " + r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user