diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java index 56f84ea7b071..f5689b745652 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java @@ -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 conditionsToNegate = negation.getCondition().getDependentConditions(); - - for (ListIterator 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 conditionsToNegate = negation.getCondition().getDependentConditions(); + for (ListIterator iterator = result.mixins.listIterator(); iterator.hasNext(); ) { + Mixin mixin = iterator.next(); + if (conditionsToNegate.contains(mixin.myCondition)) { + iterator.set(mixin.negate()); } } - return type; + return result; } @Nullable diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/InferenceCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/InferenceCache.java index 9dabb18af14c..a497bbf59f7a 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/InferenceCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/InferenceCache.java @@ -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> collectRequiredInstructions(@NotNull Instruction instruction, diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeDfaInstance.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeDfaInstance.java index e80405a28b3c..7ebb963de60c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeDfaInstance.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypeDfaInstance.java @@ -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 { 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 { 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 { private void handleArgument(TypeDfaState state, ArgumentsInstruction instruction, String variableName, Collection 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 { 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 entry : state.getVarTypes().entrySet()) { + entry.setValue(entry.getValue().negate(negation)); + } + } + @Override @NotNull public TypeDfaState initial() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java index bf62824e0476..b665b4ea779c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java @@ -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 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 getBindings(Instruction instruction) { - HashMap map = ContainerUtil.newHashMap(); + Map getBindings() { + HashMap map = ContainerUtil.newHashMap(); for (Map.Entry 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; }