From 031fbb8fdf9d83b9d652b4d19d123c1381547564 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 31 Aug 2012 22:00:51 +0200 Subject: [PATCH] context-dependent resolve and type inference, restore TypeDfaInstance but don't activate yet --- .../GroovyExpectedTypesProvider.java | 24 ++-- .../lang/psi/impl/InferenceContext.java | 123 ++++++++++++++++ .../lang/psi/impl/TypeInferenceHelper.java | 131 ++++++++++++++---- .../GrAssignmentExpressionImpl.java | 3 +- .../expressions/GrBinaryExpressionImpl.java | 3 +- .../GrReferenceExpressionImpl.java | 18 +-- .../GrThisSuperReferenceExpressionBase.java | 6 +- .../arithmetic/GrUnaryExpressionImpl.java | 3 +- .../expressions/path/GrIndexPropertyImpl.java | 2 +- .../types/GrSafeCastExpressionImpl.java | 3 +- .../types/GrCodeReferenceElementImpl.java | 7 +- 11 files changed, 259 insertions(+), 64 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/InferenceContext.java diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java index 37671b6eafed..c6a8a4dc4830 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java @@ -15,12 +15,12 @@ */ package org.jetbrains.plugins.groovy.lang.psi.expectedTypes; -import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.*; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; @@ -49,6 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; @@ -64,16 +65,11 @@ import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*; * @author ven */ public class GroovyExpectedTypesProvider { - private static final Key> CACHED_EXPECTED_TYPES = Key.create("CACHED_EXPECTED_TYPES"); - - private GroovyExpectedTypesProvider() { - } public static TypeConstraint[] calculateTypeConstraints(@NotNull final GrExpression expression) { - CachedValue cached = expression.getUserData(CACHED_EXPECTED_TYPES); - if (cached == null) { - expression.putUserData(CACHED_EXPECTED_TYPES, cached = CachedValuesManager.getManager(expression.getProject()).createCachedValue(new CachedValueProvider() { - public Result compute() { + return TypeInferenceHelper.getCurrentContext().getCachedValue(expression, new Computable() { + @Override + public TypeConstraint[] compute() { MyCalculator calculator = new MyCalculator(expression); final PsiElement parent = expression.getParent(); if (parent instanceof GroovyPsiElement) { @@ -91,14 +87,12 @@ public class GroovyExpectedTypesProvider { if (!custom.isEmpty()) { custom.addAll(0, Arrays.asList(result)); - return Result.create(custom.toArray(new TypeConstraint[custom.size()]), PsiModificationTracker.MODIFICATION_COUNT); + return custom.toArray(new TypeConstraint[custom.size()]); } - return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); + return result; } - }, false)); - } - return cached.getValue(); + }); } public static Set getDefaultExpectedTypes(@NotNull GrExpression element) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/InferenceContext.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/InferenceContext.java new file mode 100644 index 000000000000..568e19afdf1c --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/InferenceContext.java @@ -0,0 +1,123 @@ +/* + * Copyright 2000-2012 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 org.jetbrains.plugins.groovy.lang.psi.impl; + +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiPolyVariantReference; +import com.intellij.psi.PsiType; +import com.intellij.psi.ResolveResult; +import com.intellij.psi.impl.source.resolve.ResolveCache; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; + +import java.util.Map; + +import static com.intellij.util.containers.ContainerUtil.newHashMap; + +/** + * @author peter + */ +public interface InferenceContext { + InferenceContext TOP_CONTEXT = new InferenceContext() { + @Nullable + @Override + public PsiType getVariableType(@NotNull GrReferenceExpression ref) { + return TypeInferenceHelper.getInferredType(ref); + } + + @Override + public T getCachedValue(@NotNull GroovyPsiElement element, final Computable computable) { + return CachedValuesManager.getManager(element.getProject()).getCachedValue(element, new CachedValueProvider() { + @Nullable + @Override + public Result compute() { + return Result.create(computable.compute(), PsiModificationTracker.MODIFICATION_COUNT); + } + }); + } + + @Override + public GroovyResolveResult[] multiResolve(@NotNull T ref, + boolean incomplete, + ResolveCache.PolyVariantResolver resolver) { + ResolveResult[] results = ResolveCache.getInstance(ref.getElement().getProject()).resolveWithCaching(ref, resolver, true, incomplete); + return results.length == 0 ? GroovyResolveResult.EMPTY_ARRAY : (GroovyResolveResult[])results; + } + }; + + @Nullable + PsiType getVariableType(@NotNull GrReferenceExpression ref); + + T getCachedValue(@NotNull GroovyPsiElement element, Computable computable); + + GroovyResolveResult[] multiResolve(@NotNull T ref, boolean incomplete, ResolveCache.PolyVariantResolver resolver); + + class PartialContext implements InferenceContext { + private final Map myTypes; + private final Map> myCache = newHashMap(); + + public PartialContext(Map types) { + myTypes = types; + } + + @Nullable + @Override + public PsiType getVariableType(@NotNull GrReferenceExpression ref) { + return myTypes.get(ref.getReferenceName()); + } + + @Override + public T getCachedValue(@NotNull GroovyPsiElement element, Computable computable) { + return _getCachedValue(element, computable, computable.getClass()); + } + + private T _getCachedValue(PsiElement element, Computable computable, Object key) { + Map map = myCache.get(element); + if (map == null) { + myCache.put(element, map = newHashMap()); + } + if (map.containsKey(key)) { + //noinspection unchecked + return (T)map.get(key); + } + + T result = computable.compute(); + map.put(key, result); + return result; + } + + @Override + public GroovyResolveResult[] multiResolve(@NotNull final T ref, + final boolean incomplete, + final ResolveCache.PolyVariantResolver resolver) { + return _getCachedValue(ref.getElement(), new Computable() { + @Override + public GroovyResolveResult[] compute() { + return (GroovyResolveResult[])resolver.resolve(ref, incomplete); + } + }, Pair.create(incomplete, resolver.getClass())); + } + } + +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/TypeInferenceHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/TypeInferenceHelper.java index fe29c6ad3927..c9940bca442e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/TypeInferenceHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/TypeInferenceHelper.java @@ -16,13 +16,12 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.NullableComputable; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.RecursionManager; -import com.intellij.openapi.util.TextRange; +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.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; @@ -38,9 +37,11 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstru 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; +import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.DefinitionMap; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice; +import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypesSemilattice; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import java.util.*; @@ -50,8 +51,24 @@ import java.util.*; */ @SuppressWarnings("UtilityClassWithoutPrivateConstructor") public class TypeInferenceHelper { - private static final Logger LOG = Logger.getInstance(TypeInferenceHelper.class); + private static final ThreadLocal ourInferenceContext = new ThreadLocal(); + + public static T doInference(Map bindings, Computable computation) { + InferenceContext old = ourInferenceContext.get(); + ourInferenceContext.set(new InferenceContext.PartialContext(bindings)); + try { + return computation.compute(); + } + finally { + ourInferenceContext.set(old); + } + } + + public static InferenceContext getCurrentContext() { + InferenceContext context = ourInferenceContext.get(); + return context != null ? context : InferenceContext.TOP_CONTEXT; + } @Nullable public static PsiType getInferredType(@NotNull final GrReferenceExpression refExpr) { @@ -73,7 +90,6 @@ public class TypeInferenceHelper { if (type == null) return null; return type.getResultType(); } - }); } @@ -90,6 +106,34 @@ public class TypeInferenceHelper { return type != null ? type.getResultType() : null; } + + @Nullable + public static PsiType getInferredTypeNew(@NotNull final GrReferenceExpression refExpr) { + final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(refExpr); + if (scope == null) return null; + + final Instruction[] flow = scope.getControlFlow(); + return inferVariableType(refExpr.getReferenceName(), scope, flow, findInstruction(refExpr, flow)); + } + + @Nullable + public static PsiType getInferredTypeNew(@NotNull PsiElement place, String variableName) { + final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(place); + if (scope == null) return null; + + final Instruction[] flow = scope.getControlFlow(); + return inferVariableType(variableName, scope, flow, findInstructionAt(place, flow)); + } + + private static PsiType inferVariableType(String variableName, GrControlFlowOwner scope, Instruction[] flow, Instruction instruction) { + if (instruction == null) return null; + + ArrayList> list = performTypeDfa(scope, flow); + if (list == null) return null; + + return list.get(instruction.num()).get(variableName); + } + public static boolean isTooComplexTooAnalyze(GrControlFlowOwner scope) { return getDefUseMaps(scope) == null; } @@ -298,31 +342,58 @@ public class TypeInferenceHelper { return null; } - /*@Nullable - private static PsiType getInferredType(@NotNull String varName, - @NotNull Instruction instruction, - @NotNull GrControlFlowOwner scope) { - final ArrayList> dfaResult = getDefUseMaps(scope); - - if (dfaResult == null) return null; - - - final Map allDefs = dfaResult.get(instruction.num()); - final DFAType dfaType = allDefs.get(varName); - if (dfaType == null) return null; - - return dfaType.getType(); + @Nullable + private static ArrayList> performTypeDfa(GrControlFlowOwner owner, Instruction[] flow) { + final TypeDfaInstance dfaInstance = new TypeDfaInstance(owner); + final TypesSemilattice semilattice = new TypesSemilattice(owner.getManager()); + final DFAEngine> engine = new DFAEngine>(flow, dfaInstance, semilattice); + return engine.performDFAWithTimeout(); } - private static ArrayList> getDefUseMaps(final GrControlFlowOwner scope) { - return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider>>() { - @Override - public Result>> compute() { - final Instruction[] flow = scope.getControlFlow(); - final DFAEngine> engine = new DFAEngine>(flow, new TypeDFAInstance(), new TypesSemilattice(scope.getManager())); - final ArrayList> result = engine.performDFAWithTimeout(); - return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT); + static class TypeDfaInstance implements DfaInstance> { + private final PsiElement scope; + + TypeDfaInstance(PsiElement scope) { + this.scope = scope; + } + + public void fun(final Map map, final Instruction instruction) { + if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) { + final PsiElement element = instruction.getElement(); + if (element != null) { + map.put(((ReadWriteVariableInstruction)instruction).getVariableName(), doInference(map, new Computable() { + @Override + public PsiType compute() { + return TypesUtil.boxPrimitiveType(getInitializerType(element), scope.getManager(), scope.getResolveScope()); + } + })); + } } - }); - }*/ + if (instruction instanceof ArgumentInstruction) { + final String varName = ((MixinTypeInstruction)instruction).getVariableName(); + map.put(varName, doInference(map, new Computable() { + @Override + public PsiType compute() { + PsiType original = map.get(varName); + final PsiType mixin = ((MixinTypeInstruction)instruction).inferMixinType(); + if (mixin == null) return original; + if (original == null) return mixin; + return PsiIntersectionType.createIntersection(original, mixin); + } + })); + } + } + + @NotNull + public Map initial() { + return ContainerUtil.newHashMap(); + } + + public boolean isForward() { + return true; + } + + } + + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java index 57a25fc248c9..83fdf74493d8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java @@ -39,6 +39,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrI import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -136,7 +137,7 @@ public class GrAssignmentExpressionImpl extends GrExpressionImpl implements GrAs @NotNull @Override public GroovyResolveResult[] multiResolve(boolean incompleteCode) { - return (GroovyResolveResult[])ResolveCache.getInstance(getProject()).resolveWithCaching(this, RESOLVER, false, incompleteCode); + return TypeInferenceHelper.getCurrentContext().multiResolve(this, incompleteCode, RESOLVER); } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java index d80b8201284c..9cd7ef9059a0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrBinaryExpressionImpl.java @@ -35,6 +35,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinary import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; /** @@ -115,7 +116,7 @@ public abstract class GrBinaryExpressionImpl extends GrExpressionImpl implements @NotNull @Override public GroovyResolveResult[] multiResolve(boolean incompleteCode) { - return (GroovyResolveResult[])ResolveCache.getInstance(getProject()).resolveWithCaching(this, RESOLVER, false, incompleteCode); + return TypeInferenceHelper.getCurrentContext().multiResolve(this, incompleteCode, RESOLVER); } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index d3cbae702d16..1caa4f92e9f5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.NullableComputable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.RecursionManager; import com.intellij.openapi.util.text.StringUtil; @@ -314,13 +315,12 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl>() { - @Override - public Result> compute() { - return Result.create(doResolveByShape(false, null), PsiModificationTracker.MODIFICATION_COUNT); - } - }); + return TypeInferenceHelper.getCurrentContext().getCachedValue(this, new NullableComputable>() { + @Override + public Pair compute() { + return doResolveByShape(false, null); + } + }); } private Pair doResolveByShape(boolean allVariants, @Nullable GrExpression upToArgument) { @@ -593,7 +593,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl