mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DfaTypeValue -> DfaFactMapValue followup fixes; better cast processing
This commit is contained in:
+4
-2
@@ -1435,8 +1435,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private void throwException(PsiType ref, @Nullable PsiElement anchor) {
|
||||
throwException(new ExceptionTransfer(myFactory.createDfaType(ref)), anchor);
|
||||
private void throwException(@Nullable PsiType ref, @Nullable PsiElement anchor) {
|
||||
if (ref != null) {
|
||||
throwException(new ExceptionTransfer(myFactory.createDfaType(ref)), anchor);
|
||||
}
|
||||
}
|
||||
|
||||
private void throwException(ExceptionTransfer kind, @Nullable PsiElement anchor) {
|
||||
|
||||
+26
-3
@@ -29,15 +29,38 @@ public interface DfaMemoryState {
|
||||
@NotNull
|
||||
DfaMemoryState createClosureState();
|
||||
|
||||
DfaValue pop();
|
||||
DfaValue peek();
|
||||
/**
|
||||
* Pops single value from the top of the stack and returns it
|
||||
* @return popped value
|
||||
* @throws com.intellij.codeInspection.dataFlow.instructions.EmptyStackInstruction if stack is empty
|
||||
*/
|
||||
@NotNull DfaValue pop();
|
||||
|
||||
/**
|
||||
* Reads a value from the top of the stack without popping it
|
||||
* @return top of stack value
|
||||
* @throws com.intellij.codeInspection.dataFlow.instructions.EmptyStackInstruction if stack is empty
|
||||
*/
|
||||
@NotNull DfaValue peek();
|
||||
|
||||
/**
|
||||
* Pushes given value to the stack
|
||||
* @param value to push
|
||||
*/
|
||||
void push(@NotNull DfaValue value);
|
||||
|
||||
void emptyStack();
|
||||
|
||||
void setVarValue(DfaVariableValue var, DfaValue value);
|
||||
|
||||
boolean applyInstanceofOrNull(DfaValue operand, DfaPsiType type);
|
||||
/**
|
||||
* Ensures that top-of-stack value is either null or belongs to the supplied type
|
||||
*
|
||||
* @param type the type to cast to
|
||||
* @return true if cast is successful; false if top-of-stack value type is incompatible with supplied type
|
||||
* @throws com.intellij.codeInspection.dataFlow.instructions.EmptyStackInstruction if stack is empty
|
||||
*/
|
||||
boolean castTopOfStack(DfaPsiType type);
|
||||
|
||||
boolean applyCondition(DfaValue dfaCond);
|
||||
|
||||
|
||||
+25
-15
@@ -221,12 +221,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue pop() {
|
||||
myCachedHash = null;
|
||||
return myStack.pop();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaValue peek() {
|
||||
return myStack.peek();
|
||||
@@ -275,7 +277,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
|
||||
if (getVariableState(var).isNotNull()) {
|
||||
DfaConstValue dfaNull = myFactory.getConstFactory().getNull();
|
||||
applyCondition(myFactory.getRelationFactory().createRelation(var, RelationType.NE, dfaNull));
|
||||
applyRelation(var, dfaNull, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,20 +687,31 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyInstanceofOrNull(DfaValue operand, DfaPsiType type) {
|
||||
DfaValue left = unwrap(operand);
|
||||
public boolean castTopOfStack(DfaPsiType type) {
|
||||
DfaValue value = unwrap(peek());
|
||||
|
||||
if (!(left instanceof DfaVariableValue)) return true;
|
||||
DfaFactMap facts = null;
|
||||
if (value instanceof DfaVariableValue) {
|
||||
DfaVariableValue dfaVar = (DfaVariableValue)value;
|
||||
|
||||
DfaVariableValue dfaVar = (DfaVariableValue)left;
|
||||
|
||||
if (isUnknownState(dfaVar) || isNull(dfaVar)) return true;
|
||||
DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue(type);
|
||||
if (newState != null) {
|
||||
setVariableState(dfaVar, newState);
|
||||
return true;
|
||||
if (isNull(dfaVar)) return true;
|
||||
if (isUnknownState(dfaVar)) {
|
||||
facts = getVariableState(dfaVar).myFactMap;
|
||||
} else {
|
||||
DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue(type);
|
||||
if (newState == null) return false;
|
||||
setVariableState(dfaVar, newState);
|
||||
}
|
||||
} else if (value instanceof DfaFactMapValue) {
|
||||
facts = ((DfaFactMapValue)value).getFacts();
|
||||
}
|
||||
return false;
|
||||
if (facts != null) {
|
||||
DfaFactMap newFacts = TypeConstraint.withInstanceOf(facts, type);
|
||||
if (newFacts == null) return false;
|
||||
pop();
|
||||
push(myFactory.getFactFactory().createValue(newFacts));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> void setFact(DfaValue target, DfaFactType<T> factType, T fact) {
|
||||
@@ -926,9 +939,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!isNegated && dfaRight instanceof DfaFactMapValue) {
|
||||
return applyFacts(dfaLeft, ((DfaFactMapValue)dfaRight).getFacts());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-4
@@ -46,10 +46,7 @@ class DfaVariableState {
|
||||
@Nullable
|
||||
DfaVariableState withInstanceofValue(@NotNull DfaPsiType dfaType) {
|
||||
if (dfaType.getPsiType() instanceof PsiPrimitiveType) return this;
|
||||
TypeConstraint typeConstraint = getTypeConstraint();
|
||||
TypeConstraint newTypeConstraint = typeConstraint.withInstanceofValue(dfaType);
|
||||
if (newTypeConstraint == null) return null;
|
||||
return withFact(DfaFactType.TYPE_CONSTRAINT, newTypeConstraint);
|
||||
return withFacts(TypeConstraint.withInstanceOf(myFactMap, dfaType));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+4
-6
@@ -311,15 +311,13 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
@Override
|
||||
public DfaInstructionState[] visitTypeCast(TypeCastInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
final DfaValueFactory factory = runner.getFactory();
|
||||
DfaValue dfaExpr = factory.createValue(instruction.getCasted());
|
||||
if (dfaExpr != null) {
|
||||
if (!memState.applyInstanceofOrNull(dfaExpr, factory.createDfaType(instruction.getCastTo()))) {
|
||||
onInstructionProducesCCE(instruction);
|
||||
}
|
||||
if (!memState.castTopOfStack(factory.createDfaType(instruction.getCastTo()))) {
|
||||
onInstructionProducesCCE(instruction);
|
||||
return DfaInstructionState.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
if (instruction.getCastTo() instanceof PsiPrimitiveType) {
|
||||
memState.push(runner.getFactory().getBoxedFactory().createUnboxed(memState.pop()));
|
||||
memState.push(factory.getBoxedFactory().createUnboxed(memState.pop()));
|
||||
}
|
||||
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
|
||||
+10
-4
@@ -21,7 +21,6 @@ import com.intellij.psi.PsiClassType;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.MoreCollectors;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -33,6 +32,8 @@ import java.util.*;
|
||||
/**
|
||||
* Immutable class representing a number of non-primitive type constraints applied to some value.
|
||||
* There are two types of constrains: value is instance of some type and value is not an instance of some type.
|
||||
* Unlike usual Java semantics, the {@code null} value is considered to be instanceof any type (non-null instanceof can be expressed
|
||||
* via additional restriction {@link DfaFactType#CAN_BE_NULL} {@code = false}).
|
||||
*/
|
||||
public final class TypeConstraint {
|
||||
/**
|
||||
@@ -101,9 +102,6 @@ public final class TypeConstraint {
|
||||
Set<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.removeAll(moreGeneric);
|
||||
newInstanceof.add(type);
|
||||
if (newInstanceof.size() == 1 && TypeUtils.isJavaLangObject(newInstanceof.iterator().next().getPsiType())) {
|
||||
newInstanceof = Collections.emptySet();
|
||||
}
|
||||
return create(newInstanceof, myNotInstanceofValues);
|
||||
}
|
||||
|
||||
@@ -211,4 +209,12 @@ public final class TypeConstraint {
|
||||
.mapKeyValue((prefix, set) -> StreamEx.of(set).joining(",", prefix, ""))
|
||||
.joining(" ");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DfaFactMap withInstanceOf(@NotNull DfaFactMap map, @NotNull DfaPsiType type) {
|
||||
TypeConstraint constraint = map.get(DfaFactType.TYPE_CONSTRAINT);
|
||||
if (constraint == null) constraint = EMPTY;
|
||||
constraint = constraint.withInstanceofValue(type);
|
||||
return constraint == null ? null : map.with(DfaFactType.TYPE_CONSTRAINT, constraint);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -16,13 +16,13 @@
|
||||
|
||||
package com.intellij.codeInspection.dataFlow.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.PsiTypeCastExpression;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
|
||||
public class TypeCastInstruction extends Instruction {
|
||||
private final PsiTypeCastExpression myCastExpression;
|
||||
@@ -51,4 +51,9 @@ public class TypeCastInstruction extends Instruction {
|
||||
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
|
||||
return visitor.visitTypeCast(this, runner, stateBefore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CAST_TO "+myCastTo.getCanonicalText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
class Test {
|
||||
Object getXyz() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
void test() {
|
||||
String x = (String)getXyz();
|
||||
if(<warning descr="Condition 'x instanceof String' is redundant and can be replaced with '!= null'">x instanceof String</warning>) {
|
||||
System.out.println("yes!");
|
||||
}
|
||||
}
|
||||
|
||||
void testUnknown() {
|
||||
while(getXyz() != null) {
|
||||
String x = (String)getXyz();
|
||||
if(<warning descr="Condition 'x instanceof String' is redundant and can be replaced with '!= null'">x instanceof String</warning>) {
|
||||
System.out.println("yes!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,4 +52,19 @@ class CovariantReturn {
|
||||
boolean testTypeGeneric(BaseGeneric<?> base) {
|
||||
return base instanceof DerivedGenericImpl && <warning descr="Condition 'base.get(null) instanceof Integer' is redundant and can be replaced with '!= null'">base.get(null) instanceof Integer</warning>;
|
||||
}
|
||||
|
||||
interface Super {
|
||||
Object get();
|
||||
}
|
||||
|
||||
interface Sub extends Super {
|
||||
String get();
|
||||
}
|
||||
|
||||
void testCast(Super s) {
|
||||
if(s instanceof Sub) {
|
||||
Integer i = (<warning descr="Casting 's.get()' to 'Integer' may produce 'java.lang.ClassCastException'">Integer</warning>)s.get();
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,22 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
class DDD {
|
||||
@Nullable
|
||||
String field;
|
||||
|
||||
@Nullable List<String> list;
|
||||
|
||||
void test2() {
|
||||
if(list == null) list = new ArrayList<>();
|
||||
unknown();
|
||||
System.out.println(list.size());
|
||||
}
|
||||
|
||||
void unknown() {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
int test() {
|
||||
return new DDD().field.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>();
|
||||
}
|
||||
|
||||
@@ -549,4 +549,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
|
||||
|
||||
public void testTryWithResourceExpressions() { doTest(); }
|
||||
public void testArrayStoreProblems() { doTest(); }
|
||||
public void testCastInstanceOf() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user