[groovy] negate types when negations instruction is encountered

This commit is contained in:
Daniil Ovchinnikov
2019-01-23 12:57:49 +03:00
parent 7489b39865
commit c1e4e46126
4 changed files with 40 additions and 27 deletions
@@ -9,7 +9,6 @@ import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.NegatingGotoInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ConditionInstruction;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -42,7 +41,7 @@ public class DFAType {
myNegated = negated;
}
Mixin negate() {
private Mixin negate() {
return new Mixin(ID, myType, myCondition, !myNegated);
}
@@ -97,24 +96,26 @@ public class DFAType {
return true;
}
@Contract("-> new")
@NotNull
public DFAType copy() {
final DFAType type = new DFAType(primary);
type.mixins.addAll(mixins);
return type;
}
@Contract("_ -> new")
@NotNull
public DFAType negate(@NotNull Instruction instruction) {
final DFAType type = new DFAType(primary);
type.mixins.addAll(mixins);
for (NegatingGotoInstruction negation: instruction.getNegatingGotoInstruction()) {
final Set<ConditionInstruction> conditionsToNegate = negation.getCondition().getDependentConditions();
for (ListIterator<Mixin> iterator = type.mixins.listIterator(); iterator.hasNext(); ) {
Mixin mixin = iterator.next();
if (conditionsToNegate.contains(mixin.myCondition)) {
iterator.set(mixin.negate());
}
public DFAType negate(@NotNull NegatingGotoInstruction negation) {
DFAType result = copy();
final Set<ConditionInstruction> conditionsToNegate = negation.getCondition().getDependentConditions();
for (ListIterator<Mixin> iterator = result.mixins.listIterator(); iterator.hasNext(); ) {
Mixin mixin = iterator.next();
if (conditionsToNegate.contains(mixin.myCondition)) {
iterator.set(mixin.negate());
}
}
return type;
return result;
}
@Nullable
@@ -1,4 +1,4 @@
// 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-2019 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 org.jetbrains.plugins.groovy.lang.psi.dataFlow.types;
import com.intellij.openapi.util.Couple;
@@ -88,8 +88,7 @@ class InferenceCache {
@Nullable
DFAType getCachedInferredType(@NotNull String variableName, @NotNull Instruction instruction) {
DFAType dfaType = myVarTypes.get().get(instruction.num()).getVariableType(variableName);
return dfaType == null ? null : dfaType.negate(instruction);
return myVarTypes.get().get(instruction.num()).getVariableType(variableName);
}
private Couple<Set<Instruction>> collectRequiredInstructions(@NotNull Instruction instruction,
@@ -10,6 +10,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.MixinTypeInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.NegatingGotoInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ArgumentsInstruction;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType;
@@ -47,6 +48,9 @@ class TypeDfaInstance implements DfaInstance<TypeDfaState> {
else if (instruction instanceof ArgumentsInstruction) {
handleArguments(state, (ArgumentsInstruction)instruction);
}
else if (instruction instanceof NegatingGotoInstruction) {
handleNegation(state, (NegatingGotoInstruction)instruction);
}
}
private void handleMixin(@NotNull final TypeDfaState state, @NotNull final MixinTypeInstruction instruction) {
@@ -57,7 +61,7 @@ class TypeDfaInstance implements DfaInstance<TypeDfaState> {
ReadWriteVariableInstruction originalInstr = instruction.getInstructionToMixin(myFlow);
assert originalInstr != null && !originalInstr.isWrite();
final DFAType original = state.getOrCreateVariableType(varName).negate(originalInstr);
DFAType original = state.getOrCreateVariableType(varName);
original.addMixin(instruction.inferMixinType(), instruction.getConditionInstruction());
return original;
});
@@ -83,7 +87,7 @@ class TypeDfaInstance implements DfaInstance<TypeDfaState> {
private void handleArgument(TypeDfaState state, ArgumentsInstruction instruction, String variableName, Collection<Argument> arguments) {
updateVariableType(state, instruction, variableName, () -> {
final DFAType result = state.getOrCreateVariableType(variableName).negate(instruction);
final DFAType result = state.getOrCreateVariableType(variableName);
final GroovyResolveResult[] results = instruction.getElement().multiResolve(false);
for (GroovyResolveResult variant : results) {
if (!(variant instanceof GroovyMethodResult)) continue;
@@ -121,12 +125,18 @@ class TypeDfaInstance implements DfaInstance<TypeDfaState> {
type = computation.compute();
}
else {
type = TypeInferenceHelper.doInference(state.getBindings(instruction), computation);
type = TypeInferenceHelper.doInference(state.getBindings(), computation);
}
}
state.putType(variableName, type);
}
private static void handleNegation(@NotNull TypeDfaState state, @NotNull NegatingGotoInstruction negation) {
for (Map.Entry<String, DFAType> entry : state.getVarTypes().entrySet()) {
entry.setValue(entry.getValue().negate(negation));
}
}
@Override
@NotNull
public TypeDfaState initial() {
@@ -7,7 +7,6 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice;
@@ -54,6 +53,10 @@ class TypeDfaState {
myVarTypes = ContainerUtil.newHashMap(another.myVarTypes);
}
Map<String, DFAType> getVarTypes() {
return myVarTypes;
}
TypeDfaState mergeWith(TypeDfaState another) {
if (another.myVarTypes.isEmpty()) {
return this;
@@ -92,14 +95,14 @@ class TypeDfaState {
@NotNull
DFAType getOrCreateVariableType(String variableName) {
DFAType result = getVariableType(variableName);
return result == null ? DFAType.create(null) : result;
return result == null ? DFAType.create(null) : result.copy();
}
Map<String, PsiType> getBindings(Instruction instruction) {
HashMap<String,PsiType> map = ContainerUtil.newHashMap();
Map<String, PsiType> getBindings() {
HashMap<String, PsiType> map = ContainerUtil.newHashMap();
for (Map.Entry<String, DFAType> entry : myVarTypes.entrySet()) {
DFAType value = entry.getValue();
map.put(entry.getKey(), value == null ? null : value.negate(instruction).getResultType());
map.put(entry.getKey(), value == null ? null : value.getResultType());
}
return map;
}