mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
bring back whole-method type inference for groovy
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.groovy.lang.psi.dataFlow;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.PsiIntersectionType;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiType;
|
||||
@@ -57,6 +58,16 @@ public class DFAType {
|
||||
Mixin negate() {
|
||||
return new Mixin(ID, myType, myCondition, !myNegated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Mixin{" +
|
||||
"ID=" + ID +
|
||||
", myType=" + myType +
|
||||
", myCondition=" + myCondition +
|
||||
", myNegated=" + myNegated +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private final PsiType primary;
|
||||
@@ -68,6 +79,10 @@ public class DFAType {
|
||||
}
|
||||
|
||||
public void addMixin(@Nullable PsiType mixin, ConditionInstruction instruction) {
|
||||
if (mixin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mixins.add(new Mixin(mixin, instruction, false));
|
||||
}
|
||||
|
||||
@@ -136,7 +151,7 @@ public class DFAType {
|
||||
}
|
||||
|
||||
private static boolean eq(PsiType t1, PsiType t2) {
|
||||
return !TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2));
|
||||
return Comparing.equal(TypeConversionUtil.erasure(t1), TypeConversionUtil.erasure(t2));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -153,4 +168,12 @@ public class DFAType {
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DFAType{" +
|
||||
"primary=" + primary +
|
||||
", mixins=" + mixins +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
+36
-29
@@ -18,10 +18,8 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow.types;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiIntersectionType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -31,7 +29,10 @@ import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.InstanceOfInstruction;
|
||||
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.ReadWriteVariableInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ArgumentInstruction;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType;
|
||||
@@ -53,9 +54,9 @@ public class TypeInferenceHelper {
|
||||
private static final Logger LOG = Logger.getInstance(TypeInferenceHelper.class);
|
||||
private static final ThreadLocal<InferenceContext> ourInferenceContext = new ThreadLocal<InferenceContext>();
|
||||
|
||||
private static <T> T doInference(TypeDfaState bindings, Computable<T> computation) {
|
||||
private static <T> T doInference(Map<String, PsiType> bindings, Computable<T> computation) {
|
||||
InferenceContext old = ourInferenceContext.get();
|
||||
ourInferenceContext.set(new InferenceContext.PartialContext(bindings.getBindings()));
|
||||
ourInferenceContext.set(new InferenceContext.PartialContext(bindings));
|
||||
try {
|
||||
return computation.compute();
|
||||
}
|
||||
@@ -70,7 +71,7 @@ public class TypeInferenceHelper {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getInferredType(@NotNull final GrReferenceExpression refExpr) {
|
||||
public static PsiType getInferredTypeOld(@NotNull final GrReferenceExpression refExpr) {
|
||||
return RecursionManager.doPreventingRecursion(refExpr, true, new NullableComputable<PsiType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
@@ -92,7 +93,7 @@ public class TypeInferenceHelper {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getInferredType(@NotNull PsiElement place, String variableName) {
|
||||
public static PsiType getInferredTypeOld(@NotNull PsiElement place, String variableName) {
|
||||
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place);
|
||||
if (scope == null) return null;
|
||||
|
||||
@@ -106,7 +107,7 @@ public class TypeInferenceHelper {
|
||||
|
||||
|
||||
@Nullable
|
||||
public static PsiType getInferredTypeNew(@NotNull final GrReferenceExpression refExpr) {
|
||||
public static PsiType getInferredType(@NotNull final GrReferenceExpression refExpr) {
|
||||
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(refExpr);
|
||||
if (scope == null) return null;
|
||||
|
||||
@@ -114,7 +115,7 @@ public class TypeInferenceHelper {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getInferredTypeNew(@NotNull PsiElement place, String variableName) {
|
||||
public static PsiType getInferredType(@NotNull PsiElement place, String variableName) {
|
||||
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place);
|
||||
if (scope == null) return null;
|
||||
|
||||
@@ -344,22 +345,25 @@ public class TypeInferenceHelper {
|
||||
|
||||
@Nullable
|
||||
private static List<Map<String, PsiType>> performTypeDfa(GrControlFlowOwner owner, Instruction[] flow) {
|
||||
final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner);
|
||||
final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner, flow);
|
||||
final TypesSemilattice semilattice = new TypesSemilattice(owner.getManager());
|
||||
List<TypeDfaState> states = new DFAEngine<TypeDfaState>(flow, dfaInstance, semilattice).performDFAWithTimeout();
|
||||
return states == null ? null : ContainerUtil.map(states, new Function<TypeDfaState, Map<String, PsiType>>() {
|
||||
@Override
|
||||
public Map<String, PsiType> fun(TypeDfaState state) {
|
||||
return state.getBindings();
|
||||
}
|
||||
});
|
||||
if (states == null) return null;
|
||||
|
||||
List<Map<String, PsiType>> result = ContainerUtil.newArrayList();
|
||||
for (int i = 0; i < states.size(); i++) {
|
||||
result.add(states.get(i).getBindings(flow[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static class TypeDfaInstance implements DfaInstance<TypeDfaState> {
|
||||
private final PsiElement myScope;
|
||||
private final GrControlFlowOwner myScope;
|
||||
private final Instruction[] myFlow;
|
||||
|
||||
TypeDfaInstance(PsiElement scope) {
|
||||
TypeDfaInstance(GrControlFlowOwner scope, Instruction[] flow) {
|
||||
myScope = scope;
|
||||
myFlow = flow;
|
||||
}
|
||||
|
||||
public void fun(final TypeDfaState state, final Instruction instruction) {
|
||||
@@ -371,16 +375,19 @@ public class TypeInferenceHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleMixin(final TypeDfaState state, final MixinTypeInstruction instruction) {
|
||||
private void handleMixin(final TypeDfaState state, final MixinTypeInstruction instruction) {
|
||||
final String varName = instruction.getVariableName();
|
||||
state.putType(varName, doInference(state, new Computable<PsiType>() {
|
||||
if (varName == null) return;
|
||||
|
||||
state.putType(varName, doInference(state.getBindings(instruction), new NullableComputable<DFAType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
PsiType original = state.getBindings().get(varName);
|
||||
final PsiType mixin = instruction.inferMixinType();
|
||||
if (mixin == null) return original;
|
||||
if (original == null) return mixin;
|
||||
return PsiIntersectionType.createIntersection(original, mixin);
|
||||
public DFAType compute() {
|
||||
ReadWriteVariableInstruction originalInstr = instruction.getInstructionToMixin(myFlow);
|
||||
assert originalInstr != null && !originalInstr.isWrite();
|
||||
|
||||
DFAType original = state.getVariableType(varName).negate(originalInstr);
|
||||
original.addMixin(instruction.inferMixinType(), instruction.getConditionInstruction());
|
||||
return original;
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -388,10 +395,10 @@ public class TypeInferenceHelper {
|
||||
private void handleVariableWrite(TypeDfaState state, ReadWriteVariableInstruction instruction) {
|
||||
final PsiElement element = instruction.getElement();
|
||||
if (element != null && instruction.isWrite()) {
|
||||
state.putType(instruction.getVariableName(), doInference(state, new Computable<PsiType>() {
|
||||
state.putType(instruction.getVariableName(), doInference(state.getBindings(instruction), new Computable<DFAType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
return TypesUtil.boxPrimitiveType(getInitializerType(element), myScope.getManager(), myScope.getResolveScope());
|
||||
public DFAType compute() {
|
||||
return DFAType.create(TypesUtil.boxPrimitiveType(getInitializerType(element), myScope.getManager(), myScope.getResolveScope()));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
+27
-31
@@ -17,12 +17,14 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow.types;
|
||||
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -51,7 +53,7 @@ public class TypesSemilattice implements Semilattice<TypeDfaState> {
|
||||
}
|
||||
|
||||
class TypeDfaState {
|
||||
private final Map<String, PsiType> myVarTypes;
|
||||
private final Map<String, DFAType> myVarTypes;
|
||||
|
||||
TypeDfaState() {
|
||||
myVarTypes = ContainerUtil.newHashMap();
|
||||
@@ -62,13 +64,13 @@ class TypeDfaState {
|
||||
}
|
||||
|
||||
void joinState(TypeDfaState another, PsiManager manager) {
|
||||
for (Map.Entry<String, PsiType> entry : another.myVarTypes.entrySet()) {
|
||||
for (Map.Entry<String, DFAType> entry : another.myVarTypes.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final PsiType t1 = entry.getValue();
|
||||
final DFAType t1 = entry.getValue();
|
||||
if (myVarTypes.containsKey(name)) {
|
||||
final PsiType t2 = myVarTypes.get(name);
|
||||
final DFAType t2 = myVarTypes.get(name);
|
||||
if (t1 != null && t2 != null) {
|
||||
myVarTypes.put(name, TypesUtil.getLeastUpperBound(t1, t2, manager));
|
||||
myVarTypes.put(name, DFAType.create(t1, t2, manager));
|
||||
}
|
||||
else {
|
||||
myVarTypes.put(name, null);
|
||||
@@ -78,36 +80,30 @@ class TypeDfaState {
|
||||
}
|
||||
|
||||
boolean contentsEqual(TypeDfaState another) {
|
||||
if (myVarTypes.size() != another.myVarTypes.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String name : myVarTypes.keySet()) {
|
||||
if (!areTypesErasureEqual(another.myVarTypes, name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return myVarTypes.equals(another.myVarTypes);
|
||||
}
|
||||
|
||||
private boolean areTypesErasureEqual(Map<String, PsiType> another, String name) {
|
||||
if (!another.containsKey(name)) return false;
|
||||
final PsiType t1 = myVarTypes.get(name);
|
||||
final PsiType t2 = another.get(name);
|
||||
if (t1 == null || t2 == null) {
|
||||
if (t1 != null || t2 != null) return false;
|
||||
}
|
||||
else {
|
||||
if (!TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2))) return false;
|
||||
}
|
||||
return true;
|
||||
@NotNull
|
||||
DFAType getVariableType(String variableName) {
|
||||
DFAType type = myVarTypes.get(variableName);
|
||||
return type == null ? DFAType.create(null) : type;
|
||||
}
|
||||
|
||||
Map<String, PsiType> getBindings() {
|
||||
return myVarTypes;
|
||||
Map<String, PsiType> getBindings(Instruction instruction) {
|
||||
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());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
void putType(String variableName, PsiType type) {
|
||||
void putType(String variableName, DFAType type) {
|
||||
myVarTypes.put(variableName, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TypeDfaState{" + myVarTypes + '}';
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -179,7 +179,6 @@ def bar(File file) { file.path }
|
||||
}
|
||||
|
||||
public void "test using SSA variables in a for loop"() {
|
||||
//todo RecursionManager.assertOnRecursionPrevention(testRootDisposable)
|
||||
def text = """
|
||||
def foo(List<String> list, SomeClass sc) {
|
||||
List<String> result
|
||||
@@ -190,7 +189,7 @@ ${
|
||||
bar2(s, result, sc)
|
||||
bar3(foo:s, bar:result, sc)
|
||||
sc.someMethod(s)
|
||||
''' * 2
|
||||
''' * 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user