Refactoring: moved capturing in closure check to call resolver extension

This commit is contained in:
Svetlana Isakova
2014-12-26 18:16:33 +03:00
parent bc060dd145
commit 509a8adae5
6 changed files with 84 additions and 55 deletions
@@ -301,12 +301,7 @@ public class CallResolver {
}
TracingStrategy tracing = TracingStrategyImpl.create(functionReference, context.call);
OverloadResolutionResultsImpl<FunctionDescriptor> results = doResolveCallOrGetCachedResults(
context, prioritizedTasks, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing);
if (calleeExpression instanceof JetSimpleNameExpression) {
ExpressionTypingUtils.checkCapturingInClosure((JetSimpleNameExpression) calleeExpression, context.trace, context.scope);
}
return results;
return doResolveCallOrGetCachedResults(context, prioritizedTasks, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing);
}
public OverloadResolutionResults<FunctionDescriptor> resolveCallWithKnownCandidate(
@@ -27,7 +27,11 @@ import java.util.*;
public class CallResolverExtensionProvider {
private final static CompositeExtension DEFAULT =
new CompositeExtension(Arrays.asList(new NeedSyntheticCallResolverExtension(), new ReifiedTypeParameterSubstitutionCheck()));
new CompositeExtension(Arrays.asList(
new NeedSyntheticCallResolverExtension(),
new ReifiedTypeParameterSubstitutionCheck(),
new CapturingInClosureExtension()
));
private WeakReference<Map<DeclarationDescriptor, List<CallResolverExtension>>> extensionsCache;
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2014 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.jet.lang.resolve.calls.extensions
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.jet.lang.resolve.BindingTrace
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE
import org.jetbrains.jet.lang.types.expressions.CaptureKind
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import org.jetbrains.jet.lang.psi.JetFunctionLiteral
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
import org.jetbrains.jet.lang.resolve.calls.callUtil.*
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall
class CapturingInClosureExtension : CallResolverExtension {
override fun <F : CallableDescriptor> run(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
val variableResolvedCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.variableCall else resolvedCall
val variableDescriptor = variableResolvedCall.getResultingDescriptor() as? VariableDescriptor
if (variableDescriptor != null) {
checkCapturingInClosure(variableDescriptor, context.trace, context.scope)
}
}
private fun checkCapturingInClosure(variable: VariableDescriptor, trace: BindingTrace, scope: JetScope) {
val variableParent = variable.getContainingDeclaration()
val scopeContainer = scope.getContainingDeclaration()
if (scopeContainer != variableParent && variableParent is CallableDescriptor) {
if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
val inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent)
trace.record(CAPTURED_IN_CLOSURE, variable, if (inline) CaptureKind.INLINE_ONLY else CaptureKind.NOT_INLINE)
}
}
}
private fun isCapturedInInline(
context: BindingContext, scopeContainer: DeclarationDescriptor, variableParent: DeclarationDescriptor
): Boolean {
val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer)
if (scopeDeclaration !is JetFunctionLiteral) return false
val parent = scopeDeclaration.getParent()
assert(parent is JetFunctionLiteralExpression) { "parent of JetFunctionLiteral is " + parent }
val resolvedCall = (parent as JetFunctionLiteralExpression).getParentResolvedCall(context, true)
if (resolvedCall == null) return false
val callable = resolvedCall.getResultingDescriptor()
if (callable is SimpleFunctionDescriptor && callable.getInlineStrategy().isInline()) {
val scopeContainerParent = scopeContainer.getContainingDeclaration()
assert(scopeContainerParent != null) { "parent is null for " + scopeContainer }
return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent)
}
return false
}
}
@@ -101,7 +101,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
CallExpressionResolver callExpressionResolver = components.expressionTypingServices.getCallExpressionResolver();
JetTypeInfo typeInfo = callExpressionResolver.getSimpleNameExpressionTypeInfo(expression, NO_RECEIVER, null, context);
JetType type = DataFlowUtils.checkType(typeInfo.getType(), expression, context);
ExpressionTypingUtils.checkCapturingInClosure(expression, context.trace, context.scope);
return JetTypeInfo.create(type, typeInfo.getDataFlowInfo()); // TODO : Extensions to this
}
@@ -32,13 +32,10 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
import org.jetbrains.jet.lang.resolve.dataClassUtils.DataClassUtilsPackage;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ClassReceiver;
@@ -169,48 +166,6 @@ public class ExpressionTypingUtils {
}
}
private static boolean isCapturedInInline(
@NotNull BindingContext context,
@NotNull DeclarationDescriptor scopeContainer,
@NotNull DeclarationDescriptor variableParent
) {
PsiElement scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer);
if (!(scopeDeclaration instanceof JetFunctionLiteral)) {
return false;
}
PsiElement parent = scopeDeclaration.getParent();
assert parent instanceof JetFunctionLiteralExpression : "parent of JetFunctionLiteral is " + parent;
ResolvedCall<?> resolvedCall = CallUtilPackage.getParentResolvedCall((JetFunctionLiteralExpression) parent, context, true);
if (resolvedCall == null) {
return false;
}
CallableDescriptor callable = resolvedCall.getResultingDescriptor();
if (callable instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) callable).getInlineStrategy().isInline()) {
DeclarationDescriptor scopeContainerParent = scopeContainer.getContainingDeclaration();
assert scopeContainerParent != null : "parent is null for " + scopeContainer;
return scopeContainerParent == variableParent || isCapturedInInline(context, scopeContainerParent, variableParent);
}
else {
return false;
}
}
public static void checkCapturingInClosure(JetSimpleNameExpression expression, BindingTrace trace, JetScope scope) {
VariableDescriptor variable = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), expression, true);
if (variable != null) {
DeclarationDescriptor variableParent = variable.getContainingDeclaration();
DeclarationDescriptor scopeContainer = scope.getContainingDeclaration();
if (scopeContainer != variableParent && variableParent instanceof CallableDescriptor) {
if (trace.get(CAPTURED_IN_CLOSURE, variable) != CaptureKind.NOT_INLINE) {
boolean inline = isCapturedInInline(trace.getBindingContext(), scopeContainer, variableParent);
trace.record(CAPTURED_IN_CLOSURE, variable, inline ? CaptureKind.INLINE_ONLY : CaptureKind.NOT_INLINE);
}
}
}
}
@NotNull
public OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(
@NotNull ExpressionTypingContext context,
@@ -102,8 +102,7 @@ class VariablesHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
String msg = ((VariableDescriptor) descriptor).isVar()
? "Wrapped into a reference object to be modified when captured in a closure"
: "Value captured in a closure";
holder.createInfoAnnotation(elementToHighlight, msg).setTextAttributes(
JetHighlightingColors.WRAPPED_INTO_REF);
holder.createInfoAnnotation(elementToHighlight, msg).setTextAttributes(JetHighlightingColors.WRAPPED_INTO_REF);
}
if (descriptor instanceof LocalVariableDescriptor) {