mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
TypeConstraint as DfaFactType
This commit is contained in:
@@ -133,6 +133,48 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return fact.toString();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This fact represents a set of possible types of this value
|
||||
* {@link TypeConstraint#EMPTY} value is equivalent to absent fact (not constrained)
|
||||
*/
|
||||
public static final DfaFactType<TypeConstraint> TYPE_CONSTRAINT = new DfaFactType<TypeConstraint>("Type") {
|
||||
@Nullable
|
||||
@Override
|
||||
TypeConstraint fromDfaValue(DfaValue value) {
|
||||
if(value instanceof DfaTypeValue) {
|
||||
TypeConstraint constraint = TypeConstraint.EMPTY.withInstanceofValue((DfaTypeValue)value);
|
||||
return constraint == null || constraint.isEmpty() ? null : constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSuper(@Nullable TypeConstraint superFact, @Nullable TypeConstraint subFact) {
|
||||
return superFact == null || (subFact != null && superFact.isSuperStateOf(subFact));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TypeConstraint intersectFacts(@NotNull TypeConstraint left, @NotNull TypeConstraint right) {
|
||||
for (DfaPsiType type : right.getInstanceofValues()) {
|
||||
left = left.withInstanceofValue(type);
|
||||
if (left == null) return null;
|
||||
}
|
||||
for (DfaPsiType type : right.getNotInstanceofValues()) {
|
||||
left = left.withNotInstanceofValue(type);
|
||||
if (left == null) return null;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TypeConstraint unionFacts(@NotNull TypeConstraint left, @NotNull TypeConstraint right) {
|
||||
if(left.isSuperStateOf(right)) return left;
|
||||
if(right.isSuperStateOf(left)) return right;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private DfaFactType(String name) {
|
||||
super("DfaFactType: " + name);
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -56,13 +55,6 @@ public interface DfaMemoryState {
|
||||
@Nullable
|
||||
<T> T getValueFact(@NotNull DfaFactType<T> factType, @NotNull DfaValue value);
|
||||
|
||||
/**
|
||||
* @param value to determine its type
|
||||
* @return value type at this state if known (possibly erased)
|
||||
*/
|
||||
@Nullable
|
||||
PsiType getValueType(DfaValue value);
|
||||
|
||||
void flushFields();
|
||||
|
||||
void flushVariable(DfaVariableValue variable);
|
||||
|
||||
-17
@@ -1068,23 +1068,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return factType.fromDfaValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiType getValueType(DfaValue value) {
|
||||
if (value instanceof DfaTypeValue) {
|
||||
return ((DfaTypeValue)value).getDfaType().getPsiType();
|
||||
}
|
||||
if (value instanceof DfaVariableValue) {
|
||||
DfaVariableState state = getVariableState((DfaVariableValue)value);
|
||||
Set<DfaPsiType> values = state.getInstanceofValues();
|
||||
if (!values.isEmpty()) {
|
||||
return values.iterator().next().getPsiType();
|
||||
}
|
||||
return ((DfaVariableValue)value).getVariableType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DfaValue resolveVariableValue(DfaVariableValue var) {
|
||||
DfaConstValue constValue = getConstantValue(var);
|
||||
|
||||
+22
-115
@@ -20,117 +20,49 @@ import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Objects;
|
||||
|
||||
class DfaVariableState {
|
||||
@NotNull final Set<DfaPsiType> myInstanceofValues;
|
||||
@NotNull final Set<DfaPsiType> myNotInstanceofValues;
|
||||
@NotNull final DfaFactMap myFactMap;
|
||||
private final int myHash;
|
||||
|
||||
DfaVariableState(@NotNull DfaVariableValue dfaVar) {
|
||||
this(Collections.emptySet(), Collections.emptySet(), dfaVar.getInherentFacts());
|
||||
this(dfaVar.getInherentFacts());
|
||||
}
|
||||
|
||||
public boolean isSuperStateOf(DfaVariableState that) {
|
||||
if(!that.myNotInstanceofValues.containsAll(myNotInstanceofValues)) return false;
|
||||
if(!that.myInstanceofValues.containsAll(myInstanceofValues)) return false;
|
||||
return myFactMap.isSuperStateOf(that.myFactMap);
|
||||
}
|
||||
|
||||
DfaVariableState(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull DfaFactMap factMap) {
|
||||
myInstanceofValues = instanceofValues;
|
||||
myNotInstanceofValues = notInstanceofValues;
|
||||
DfaVariableState(@NotNull DfaFactMap factMap) {
|
||||
myFactMap = factMap;
|
||||
myHash = Objects.hash(myInstanceofValues, myNotInstanceofValues, myFactMap);
|
||||
}
|
||||
|
||||
private boolean checkInstanceofValue(@NotNull DfaPsiType dfaType) {
|
||||
if (myInstanceofValues.contains(dfaType)) return true;
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myNotInstanceofValues) {
|
||||
if (dfaTypeValue.isAssignableFrom(dfaType)) return false;
|
||||
}
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (!dfaType.isConvertibleFrom(dfaTypeValue)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
myHash = myFactMap.hashCode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
DfaVariableState withInstanceofValue(@NotNull DfaTypeValue dfaType) {
|
||||
if (dfaType.getDfaType().getPsiType() instanceof PsiPrimitiveType) return this;
|
||||
|
||||
if (checkInstanceofValue(dfaType.getDfaType())) {
|
||||
DfaVariableState result = dfaType.isNullable() ? withFact(DfaFactType.CAN_BE_NULL, true) : this;
|
||||
List<DfaPsiType> moreGeneric = ContainerUtil.newArrayList();
|
||||
for (DfaPsiType alreadyInstanceof : myInstanceofValues) {
|
||||
if (dfaType.getDfaType().isAssignableFrom(alreadyInstanceof)) {
|
||||
return result;
|
||||
}
|
||||
if (alreadyInstanceof.isAssignableFrom(dfaType.getDfaType())) {
|
||||
moreGeneric.add(alreadyInstanceof);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.removeAll(moreGeneric);
|
||||
newInstanceof.add(dfaType.getDfaType());
|
||||
result = createCopy(newInstanceof, myNotInstanceofValues, result.myFactMap);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
TypeConstraint typeConstraint = getTypeConstraint();
|
||||
TypeConstraint newTypeConstraint = typeConstraint.withInstanceofValue(dfaType);
|
||||
if (newTypeConstraint == null) return null;
|
||||
DfaVariableState result = dfaType.isNullable() ? withFact(DfaFactType.CAN_BE_NULL, true) : this;
|
||||
return result.withFact(DfaFactType.TYPE_CONSTRAINT, newTypeConstraint);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
DfaVariableState withNotInstanceofValue(@NotNull DfaTypeValue dfaType) {
|
||||
if (myNotInstanceofValues.contains(dfaType.getDfaType())) return this;
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (dfaType.getDfaType().isAssignableFrom(dfaTypeValue)) return null;
|
||||
}
|
||||
|
||||
List<DfaPsiType> moreSpecific = ContainerUtil.newArrayList();
|
||||
for (DfaPsiType alreadyNotInstanceof : myNotInstanceofValues) {
|
||||
if (alreadyNotInstanceof.isAssignableFrom(dfaType.getDfaType())) {
|
||||
return this;
|
||||
}
|
||||
if (dfaType.getDfaType().isAssignableFrom(alreadyNotInstanceof)) {
|
||||
moreSpecific.add(alreadyNotInstanceof);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.removeAll(moreSpecific);
|
||||
newNotInstanceof.add(dfaType.getDfaType());
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myFactMap);
|
||||
TypeConstraint typeConstraint = getTypeConstraint();
|
||||
TypeConstraint newTypeConstraint = typeConstraint.withNotInstanceofValue(dfaType);
|
||||
return newTypeConstraint == null ? null : withFact(DfaFactType.TYPE_CONSTRAINT, newTypeConstraint);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
DfaVariableState withoutType(@NotNull DfaPsiType type) {
|
||||
if (myInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.remove(type);
|
||||
return createCopy(newInstanceof, myNotInstanceofValues, myFactMap);
|
||||
}
|
||||
if (myNotInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.remove(type);
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myFactMap);
|
||||
}
|
||||
return this;
|
||||
return withFact(DfaFactType.TYPE_CONSTRAINT, getTypeConstraint().withoutType(type));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
@@ -141,35 +73,16 @@ class DfaVariableState {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof DfaVariableState)) return false;
|
||||
DfaVariableState aState = (DfaVariableState) obj;
|
||||
return myHash == aState.myHash &&
|
||||
myInstanceofValues.equals(aState.myInstanceofValues) &&
|
||||
myNotInstanceofValues.equals(aState.myNotInstanceofValues) &&
|
||||
Objects.equals(myFactMap, aState.myFactMap);
|
||||
return myHash == aState.myHash && Objects.equals(myFactMap, aState.myFactMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected DfaVariableState createCopy(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull DfaFactMap factMap) {
|
||||
return new DfaVariableState(instanceofValues, notInstanceofValues, factMap);
|
||||
protected DfaVariableState createCopy(@NotNull DfaFactMap factMap) {
|
||||
return new DfaVariableState(factMap);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@NonNls StringBuilder buf = new StringBuilder("State:");
|
||||
|
||||
if (!myInstanceofValues.isEmpty()) {
|
||||
buf.append(" instanceof ").append(StringUtil.join(myInstanceofValues, ","));
|
||||
}
|
||||
|
||||
if (!myNotInstanceofValues.isEmpty()) {
|
||||
buf.append(" not instanceof ").append(StringUtil.join(myNotInstanceofValues, ","));
|
||||
}
|
||||
|
||||
String factString = myFactMap.toString();
|
||||
if(!factString.isEmpty()) {
|
||||
buf.append(" ").append(factString);
|
||||
}
|
||||
return buf.toString();
|
||||
return "State: " + myFactMap;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -189,7 +102,7 @@ class DfaVariableState {
|
||||
@NotNull
|
||||
<T> DfaVariableState withFact(DfaFactType<T> type, T value) {
|
||||
DfaFactMap factMap = myFactMap.with(type, value);
|
||||
return myFactMap.equals(factMap) ? this : createCopy(myInstanceofValues, myNotInstanceofValues, factMap);
|
||||
return myFactMap.equals(factMap) ? this : createCopy(factMap);
|
||||
}
|
||||
|
||||
<T> DfaVariableState withoutFact(DfaFactType<T> type) {
|
||||
@@ -199,9 +112,7 @@ class DfaVariableState {
|
||||
@Nullable
|
||||
<T> DfaVariableState intersectFact(DfaFactType<T> type, T value) {
|
||||
DfaFactMap factMap = myFactMap.intersect(type, value);
|
||||
return factMap == null
|
||||
? null
|
||||
: myFactMap.equals(factMap) ? this : createCopy(myInstanceofValues, myNotInstanceofValues, factMap);
|
||||
return factMap == null ? null : myFactMap.equals(factMap) ? this : createCopy(factMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -215,13 +126,9 @@ class DfaVariableState {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<DfaPsiType> getInstanceofValues() {
|
||||
return myInstanceofValues;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<DfaPsiType> getNotInstanceofValues() {
|
||||
return myNotInstanceofValues;
|
||||
public TypeConstraint getTypeConstraint() {
|
||||
TypeConstraint fact = getFact(DfaFactType.TYPE_CONSTRAINT);
|
||||
return fact == null ? TypeConstraint.EMPTY : fact;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+1
-3
@@ -33,7 +33,6 @@ import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -138,8 +137,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
super(factory);
|
||||
for (PsiParameter parameter : myPossiblyViolatedParameters) {
|
||||
setVariableState(getFactory().getVarFactory().createVariableValue(parameter, false),
|
||||
new DfaVariableState(Collections.emptySet(), Collections.emptySet(),
|
||||
DfaFactMap.EMPTY.with(DfaFactType.CAN_BE_NULL, true)));
|
||||
new DfaVariableState(DfaFactMap.EMPTY.with(DfaFactType.CAN_BE_NULL, true)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-14
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
|
||||
import com.intellij.lang.jvm.JvmModifier;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
@@ -477,6 +478,28 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiMethod findSpecificMethod(@NotNull PsiMethod method, @NotNull DfaMemoryState state, @Nullable DfaValue qualifier) {
|
||||
if (qualifier == null ||
|
||||
method.hasModifier(JvmModifier.STATIC) ||
|
||||
method.hasModifier(JvmModifier.FINAL) ||
|
||||
method.hasModifier(JvmModifier.PRIVATE)) {
|
||||
return method;
|
||||
}
|
||||
TypeConstraint constraint = state.getValueFact(DfaFactType.TYPE_CONSTRAINT, qualifier);
|
||||
PsiClass specificQualifierClass = PsiUtil.resolveClassInClassTypeOnly(constraint == null ? null : constraint.getPsiType());
|
||||
PsiClass qualifierClass = method.getContainingClass();
|
||||
if (specificQualifierClass != null && qualifierClass != null &&
|
||||
!specificQualifierClass.equals(qualifierClass) &&
|
||||
InheritanceUtil.isInheritorOrSelf(specificQualifierClass, qualifierClass, true)) {
|
||||
PsiMethod realMethod = specificQualifierClass.findMethodBySignature(method, true);
|
||||
if (realMethod != null) {
|
||||
return realMethod;
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DfaValue getMethodResultValue(MethodCallInstruction instruction,
|
||||
@Nullable DfaValue qualifierValue,
|
||||
@@ -520,19 +543,13 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
Nullness nullability = instruction.getReturnNullability();
|
||||
PsiMethod targetMethod = instruction.getTargetMethod();
|
||||
if (targetMethod != null) {
|
||||
PsiClass specificQualifierClass = PsiUtil.resolveClassInClassTypeOnly(state.getValueType(qualifierValue));
|
||||
PsiClass qualifierClass = targetMethod.getContainingClass();
|
||||
if (specificQualifierClass != null && qualifierClass != null &&
|
||||
!specificQualifierClass.equals(qualifierClass) &&
|
||||
InheritanceUtil.isInheritorOrSelf(specificQualifierClass, qualifierClass, true)) {
|
||||
PsiMethod realMethod = specificQualifierClass.findMethodBySignature(targetMethod, true);
|
||||
if (realMethod != null && realMethod != targetMethod) {
|
||||
nullability = DfaPsiUtil.getElementNullability(type, realMethod);
|
||||
PsiType returnType = realMethod.getReturnType();
|
||||
if(returnType != null && TypeConversionUtil.erasure(type).isAssignableFrom(returnType)) {
|
||||
// possibly covariant return type
|
||||
type = returnType;
|
||||
}
|
||||
PsiMethod realMethod = findSpecificMethod(targetMethod, state, qualifierValue);
|
||||
if (realMethod != targetMethod) {
|
||||
nullability = DfaPsiUtil.getElementNullability(type, realMethod);
|
||||
PsiType returnType = realMethod.getReturnType();
|
||||
if (returnType != null && TypeConversionUtil.erasure(type).isAssignableFrom(returnType)) {
|
||||
// possibly covariant return type
|
||||
type = returnType;
|
||||
}
|
||||
}
|
||||
if (nullability == Nullness.UNKNOWN) {
|
||||
@@ -700,7 +717,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return checkComparisonWithKnownValue(instruction, runner, memState, relationType, leftValue, rightValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dfaRight instanceof DfaConstValue && dfaLeft instanceof DfaVariableValue) {
|
||||
Object value = ((DfaConstValue)dfaRight).getValue();
|
||||
if (value instanceof Number) {
|
||||
|
||||
@@ -430,10 +430,11 @@ class StateMerger {
|
||||
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : states.entrySet()) {
|
||||
DfaVariableValue var = entry.getKey();
|
||||
DfaVariableState variableState = entry.getValue();
|
||||
for (DfaPsiType type : variableState.getInstanceofValues()) {
|
||||
TypeConstraint typeConstraint = variableState.getTypeConstraint();
|
||||
for (DfaPsiType type : typeConstraint.getInstanceofValues()) {
|
||||
result.add(new InstanceofFact(var, true, type));
|
||||
}
|
||||
for (DfaPsiType type : variableState.getNotInstanceofValues()) {
|
||||
for (DfaPsiType type : typeConstraint.getNotInstanceofValues()) {
|
||||
result.add(new InstanceofFact(var, false, type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.
|
||||
*/
|
||||
public final class TypeConstraint {
|
||||
/**
|
||||
* An instance representing no constraints
|
||||
*/
|
||||
public static final TypeConstraint EMPTY = new TypeConstraint(Collections.emptySet(), Collections.emptySet());
|
||||
|
||||
@NotNull private final Set<DfaPsiType> myInstanceofValues;
|
||||
@NotNull private final Set<DfaPsiType> myNotInstanceofValues;
|
||||
|
||||
private TypeConstraint(@NotNull Set<DfaPsiType> instanceofValues, @NotNull Set<DfaPsiType> notInstanceofValues) {
|
||||
myInstanceofValues = instanceofValues;
|
||||
myNotInstanceofValues = notInstanceofValues;
|
||||
}
|
||||
|
||||
private static TypeConstraint create(@NotNull Set<DfaPsiType> instanceofValues, @NotNull Set<DfaPsiType> notInstanceofValues) {
|
||||
if (instanceofValues.isEmpty() && notInstanceofValues.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
if (instanceofValues.isEmpty()) {
|
||||
instanceofValues = Collections.emptySet();
|
||||
}
|
||||
else if (instanceofValues.size() == 1) {
|
||||
instanceofValues = Collections.singleton(instanceofValues.iterator().next());
|
||||
}
|
||||
if (notInstanceofValues.isEmpty()) {
|
||||
notInstanceofValues = Collections.emptySet();
|
||||
}
|
||||
else if (notInstanceofValues.size() == 1) {
|
||||
notInstanceofValues = Collections.singleton(notInstanceofValues.iterator().next());
|
||||
}
|
||||
return new TypeConstraint(instanceofValues, notInstanceofValues);
|
||||
}
|
||||
|
||||
boolean checkInstanceofValue(@NotNull DfaPsiType dfaType) {
|
||||
if (myInstanceofValues.contains(dfaType)) return true;
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myNotInstanceofValues) {
|
||||
if (dfaTypeValue.isAssignableFrom(dfaType)) return false;
|
||||
}
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (!dfaType.isConvertibleFrom(dfaTypeValue)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withInstanceofValue(@NotNull DfaTypeValue dfaType) {
|
||||
return withInstanceofValue(dfaType.getDfaType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withInstanceofValue(@NotNull DfaPsiType type) {
|
||||
if (type.getPsiType() instanceof PsiPrimitiveType) return this;
|
||||
|
||||
if (!checkInstanceofValue(type)) {
|
||||
return null;
|
||||
}
|
||||
List<DfaPsiType> moreGeneric = new ArrayList<>();
|
||||
for (DfaPsiType alreadyInstanceof : myInstanceofValues) {
|
||||
if (type.isAssignableFrom(alreadyInstanceof)) {
|
||||
return this;
|
||||
}
|
||||
if (alreadyInstanceof.isAssignableFrom(type)) {
|
||||
moreGeneric.add(alreadyInstanceof);
|
||||
}
|
||||
}
|
||||
|
||||
Set<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.removeAll(moreGeneric);
|
||||
newInstanceof.add(type);
|
||||
return create(newInstanceof, myNotInstanceofValues);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withNotInstanceofValue(@NotNull DfaTypeValue dfaType) {
|
||||
return withNotInstanceofValue(dfaType.getDfaType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withNotInstanceofValue(DfaPsiType type) {
|
||||
if (myNotInstanceofValues.contains(type)) return this;
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
if (type.isAssignableFrom(dfaTypeValue)) return null;
|
||||
}
|
||||
|
||||
List<DfaPsiType> moreSpecific = new ArrayList<>();
|
||||
for (DfaPsiType alreadyNotInstanceof : myNotInstanceofValues) {
|
||||
if (alreadyNotInstanceof.isAssignableFrom(type)) {
|
||||
return this;
|
||||
}
|
||||
if (type.isAssignableFrom(alreadyNotInstanceof)) {
|
||||
moreSpecific.add(alreadyNotInstanceof);
|
||||
}
|
||||
}
|
||||
|
||||
Set<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.removeAll(moreSpecific);
|
||||
newNotInstanceof.add(type);
|
||||
return create(myInstanceofValues, newNotInstanceof);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
TypeConstraint withoutType(@NotNull DfaPsiType type) {
|
||||
if (myInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.remove(type);
|
||||
return create(newInstanceof, myNotInstanceofValues);
|
||||
}
|
||||
if (myNotInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.remove(type);
|
||||
return create(myInstanceofValues, newNotInstanceof);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiType getPsiType() {
|
||||
if (myInstanceofValues.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return myInstanceofValues.iterator().next().getPsiType();
|
||||
}
|
||||
|
||||
boolean isSuperStateOf(@NotNull TypeConstraint that) {
|
||||
return that.myNotInstanceofValues.containsAll(myNotInstanceofValues) && that.myInstanceofValues.containsAll(myInstanceofValues);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<DfaPsiType> getInstanceofValues() {
|
||||
return Collections.unmodifiableSet(myInstanceofValues);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<DfaPsiType> getNotInstanceofValues() {
|
||||
return Collections.unmodifiableSet(myNotInstanceofValues);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return myInstanceofValues.isEmpty() && myNotInstanceofValues.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TypeConstraint that = (TypeConstraint)o;
|
||||
return Objects.equals(myInstanceofValues, that.myInstanceofValues) &&
|
||||
Objects.equals(myNotInstanceofValues, that.myNotInstanceofValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(myInstanceofValues, myNotInstanceofValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return EntryStream.of("instanceof ", myInstanceofValues,
|
||||
"not instanceof ", myNotInstanceofValues)
|
||||
.removeValues(Set::isEmpty)
|
||||
.mapKeyValue((prefix, set) -> StreamEx.of(set).joining(",", prefix, ""))
|
||||
.joining(" ");
|
||||
}
|
||||
}
|
||||
+6
-13
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
@@ -25,8 +24,6 @@ import com.intellij.util.containers.FList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Gregory.Shrago
|
||||
*/
|
||||
@@ -74,34 +71,30 @@ class ValuableDataFlowRunner extends DataFlowRunner {
|
||||
myConcatenation = FList.emptyList();
|
||||
}
|
||||
|
||||
private ValuableDfaVariableState(Set<DfaPsiType> instanceofValues,
|
||||
Set<DfaPsiType> notInstanceofValues,
|
||||
DfaValue value,
|
||||
private ValuableDfaVariableState(DfaValue value,
|
||||
@NotNull FList<PsiExpression> concatenation,
|
||||
@NotNull DfaFactMap factMap) {
|
||||
super(instanceofValues, notInstanceofValues, factMap);
|
||||
super(factMap);
|
||||
myValue = value;
|
||||
myConcatenation = concatenation;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DfaVariableState createCopy(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull DfaFactMap factMap) {
|
||||
return new ValuableDfaVariableState(instanceofValues, notInstanceofValues, myValue, myConcatenation, factMap);
|
||||
protected DfaVariableState createCopy(@NotNull DfaFactMap factMap) {
|
||||
return new ValuableDfaVariableState(myValue, myConcatenation, factMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DfaVariableState withValue(@Nullable final DfaValue value) {
|
||||
if (value == myValue) return this;
|
||||
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, value, myConcatenation, myFactMap);
|
||||
return new ValuableDfaVariableState(value, myConcatenation, myFactMap);
|
||||
}
|
||||
|
||||
ValuableDfaVariableState withExpression(@NotNull final FList<PsiExpression> concatenation) {
|
||||
if (concatenation == myConcatenation) return this;
|
||||
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myValue, concatenation, myFactMap);
|
||||
return new ValuableDfaVariableState(myValue, concatenation, myFactMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user