All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2016 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.kotlin.resolve

import com.google.common.collect.Lists
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.checkers.OperatorCallChecker
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER
import org.jetbrains.kotlin.resolve.calls.inference.toHandle
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.ScopeUtils
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType
import org.jetbrains.kotlin.types.expressions.FakeCallResolver
import org.jetbrains.kotlin.util.OperatorNameConventions

//TODO: check for 'operator' modifier!
class DelegatedPropertyResolver(
        private val builtIns: KotlinBuiltIns,
        private val fakeCallResolver: FakeCallResolver,
        private val expressionTypingServices: ExpressionTypingServices,
        private val languageVersionSettings: LanguageVersionSettings
) {

    fun resolvePropertyDelegate(
            outerDataFlowInfo: DataFlowInfo,
            property: KtProperty,
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            propertyHeaderScope: LexicalScope,
            trace: BindingTrace
    ) {
        property.getter?.let { getter ->
            if (getter.hasBody()) trace.report(ACCESSOR_FOR_DELEGATED_PROPERTY.on(getter))
        }
        property.setter?.let { setter ->
            if (setter.hasBody()) trace.report(ACCESSOR_FOR_DELEGATED_PROPERTY.on(setter))
        }

        val initializerScope: LexicalScope =
                if (variableDescriptor is PropertyDescriptor)
                    ScopeUtils.makeScopeForPropertyInitializer(propertyHeaderScope, variableDescriptor)
                else propertyHeaderScope

        val byExpressionType = resolveDelegateExpression(delegateExpression, property, variableDescriptor, initializerScope, trace, outerDataFlowInfo)

        resolveProvideDelegateMethod(variableDescriptor, delegateExpression, byExpressionType, trace, initializerScope, outerDataFlowInfo)
        val delegateType = getResolvedDelegateType(variableDescriptor, delegateExpression, byExpressionType, trace)

        resolveGetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, initializerScope, outerDataFlowInfo)
        if (property.isVar) {
            resolveSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, initializerScope, outerDataFlowInfo)
        }
    }

    private fun getResolvedDelegateType(
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            byExpressionType: KotlinType,
            trace: BindingTrace
    ): KotlinType {
        val provideDelegateResolvedCall = trace.bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor)
        if (provideDelegateResolvedCall != null) {
            return provideDelegateResolvedCall.resultingDescriptor.returnType
                   ?: throw AssertionError("No return type fore 'provideDelegate' of ${delegateExpression.text}")
        }
        return byExpressionType
    }

    fun getGetValueMethodReturnType(
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            byExpressionType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo
    ): KotlinType? {
        resolveProvideDelegateMethod(variableDescriptor, delegateExpression, byExpressionType, trace, initializerScope, dataFlowInfo)
        val delegateType = getResolvedDelegateType(variableDescriptor, delegateExpression, byExpressionType, trace)
        resolveGetSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, initializerScope, dataFlowInfo, true)

        val resolvedCall = trace.bindingContext.get(DELEGATED_PROPERTY_RESOLVED_CALL, variableDescriptor.getter)
        return if (resolvedCall != null) resolvedCall.resultingDescriptor.returnType else null
    }

    private val isOperatorProvideDelegateSupported: Boolean
        get() = languageVersionSettings.supportsFeature(LanguageFeature.OperatorProvideDelegate)

    private fun resolveGetValueMethod(
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            delegateType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo
    ) {
        val returnType = getGetValueMethodReturnType(variableDescriptor, delegateExpression, delegateType, trace, initializerScope, dataFlowInfo)
        val propertyType = variableDescriptor.type

        /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
        if (propertyType !is DeferredType && returnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
            val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, variableDescriptor.getter)
                       ?: throw AssertionError("Call should exists for ${variableDescriptor.getter}")
            trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH.on(
                    delegateExpression, renderCall(call, trace.bindingContext), variableDescriptor.type, returnType))
        }
    }

    private fun resolveSetValueMethod(
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            delegateType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo
    ) {
        resolveGetSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace,
                                 initializerScope, dataFlowInfo, false)
    }

    private fun KtPsiFactory.createExpressionForProperty(): KtExpression {
        return createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>")
    }

    /* Resolve getValue() or setValue() methods from delegate */
    private fun resolveGetSetValueMethod(
            propertyDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            delegateType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo,
            isGet: Boolean
    ) {
        val accessor = (if (isGet) propertyDescriptor.getter else propertyDescriptor.setter)
                       ?: throw AssertionError("Delegated property should have getter/setter $propertyDescriptor ${delegateExpression.text}")

        if (trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) != null) return

        val functionResults = getGetSetValueMethod(
                propertyDescriptor, delegateExpression, delegateType, trace, initializerScope, dataFlowInfo,
                isGet = isGet, isComplete = true
        )

        if (!functionResults.isSuccess) {
            val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor)
                       ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
            reportDelegateOperatorResolutionError(trace, call, functionResults, delegateExpression, delegateType)
            return
        }

        val resultingDescriptor = functionResults.resultingDescriptor
        val resultingCall = functionResults.resultingCall

        if (!resultingDescriptor.isOperator) {
            val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor)
            if (declaration is KtProperty) {
                val delegate = declaration.delegate
                if (delegate != null) {
                    val byKeyword = delegate.byKeywordNode.psi
                    OperatorCallChecker.report(byKeyword, resultingDescriptor, trace)
                }
            }
        }

        trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall)
    }

    private fun reportDelegateOperatorResolutionError(
            trace: BindingTrace,
            delegateOperatorCall: Call,
            delegateOperatorResults: OverloadResolutionResults,
            delegateExpression: KtExpression,
            delegateType: KotlinType,
            operatorRequired: Boolean = true
    ) {
        val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext)

        when {
            delegateOperatorResults.isSingleResult ||
            delegateOperatorResults.isIncomplete ||
            delegateOperatorResults.resultCode == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES ->
                trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
            delegateOperatorResults.isAmbiguity ->
                trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls))
            else ->
                if (operatorRequired) trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType))
        }
    }

    private fun resolveProvideDelegateMethod(
            propertyDescriptor: VariableDescriptorWithAccessors,
            byExpression: KtExpression,
            byExpressionType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo
    ) {
        if (!isOperatorProvideDelegateSupported) return
        if (trace.bindingContext.get(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor) != null) return

        val provideDelegateResults = getProvideDelegateMethod(propertyDescriptor, byExpression, byExpressionType,
                                                            trace, initializerScope, dataFlowInfo)
        if (!provideDelegateResults.isSuccess) {
            val call = trace.bindingContext.get(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor)
                       ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
            reportDelegateOperatorResolutionError(trace, call, provideDelegateResults, byExpression, byExpressionType,
                                                  operatorRequired = false)
            return
        }

        val resultingDescriptor = provideDelegateResults.resultingDescriptor
        if (!resultingDescriptor.isOperator) {
            // TODO resolved 'provideDelegate' function, which is not an operator - warning?
            return
        }

        val resultingCall = provideDelegateResults.resultingCall
        trace.record(PROVIDE_DELEGATE_RESOLVED_CALL, propertyDescriptor, resultingCall)
    }

    /* Resolve getValue() or setValue() methods from delegate */
    private fun getGetSetValueMethod(
            propertyDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            delegateType: KotlinType,
            trace: BindingTrace,
            scopeForDelegate: LexicalScope,
            dataFlowInfo: DataFlowInfo,
            isGet: Boolean,
            isComplete: Boolean
    ): OverloadResolutionResults {
        val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForDelegate, propertyDescriptor)

        val accessor = (if (isGet) propertyDescriptor.getter else propertyDescriptor.setter)
                       ?: throw AssertionError("Delegated property should have getter/setter $propertyDescriptor ${delegateExpression.text}")

        val expectedType = if (isComplete && isGet && propertyDescriptor.type !is DeferredType)
            propertyDescriptor.type
        else
            TypeUtils.NO_EXPECTED_TYPE

        val context = ExpressionTypingContext.newContext(trace, delegateFunctionsScope, dataFlowInfo, expectedType)

        val hasThis = propertyDescriptor.extensionReceiverParameter != null || propertyDescriptor.dispatchReceiverParameter != null

        val arguments = Lists.newArrayList()
        val psiFactory = KtPsiFactory(delegateExpression)
        arguments.add(psiFactory.createExpression(if (hasThis) "this" else "null"))
        arguments.add(psiFactory.createExpressionForProperty())

        if (!isGet) {
            val fakeArgument = createFakeExpressionOfType(delegateExpression.project, trace,
                                                          "fakeArgument${arguments.size}",
                                                          propertyDescriptor.type) as KtReferenceExpression
            arguments.add(fakeArgument)
            val valueParameters = accessor.valueParameters
            trace.record(REFERENCE_TARGET, fakeArgument, valueParameters[0])
        }

        val functionName = if (isGet) OperatorNameConventions.GET_VALUE else OperatorNameConventions.SET_VALUE
        val receiver = ExpressionReceiver.create(delegateExpression, delegateType, trace.bindingContext)

        val resolutionResult = fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression)

        trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, resolutionResult.first)
        return resolutionResult.second
    }

    private fun getProvideDelegateMethod(
            propertyDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            delegateExpressionType: KotlinType,
            trace: BindingTrace,
            initializerScope: LexicalScope,
            dataFlowInfo: DataFlowInfo
    ): OverloadResolutionResults {
        val expectedType = TypeUtils.NO_EXPECTED_TYPE
        val context = ExpressionTypingContext.newContext(trace, initializerScope, dataFlowInfo, expectedType)
        val propertyHasReceiver = propertyDescriptor.dispatchReceiverParameter != null
        val arguments = KtPsiFactory(delegateExpression).run {
            listOf(
                    createExpression(if (propertyHasReceiver) "this" else "null"),
                    createExpressionForProperty()
            )
        }
        val functionName = OperatorNameConventions.PROVIDE_DELEGATE
        val receiver = ExpressionReceiver.create(delegateExpression, delegateExpressionType, trace.bindingContext)

        val (provideDelegateCall, provideDelegateResults) =
                fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression)
        trace.record(BindingContext.PROVIDE_DELEGATE_CALL, propertyDescriptor, provideDelegateCall)

        return provideDelegateResults
    }

    //TODO: diagnostics rendering does not belong here
    private fun renderCall(call: Call, context: BindingContext): String {
        val calleeExpression = call.calleeExpression
                               ?: throw AssertionError("CalleeExpression should exists for fake call of convention method")

        return call.valueArguments.joinToString(
                prefix = "${calleeExpression.text}(",
                postfix = ")",
                separator = ", ",
                transform = { argument ->
                    val type = context.getType(argument.getArgumentExpression()!!)!!
                    DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(type)
                }
        )
    }

    fun resolveDelegateExpression(
            delegateExpression: KtExpression,
            property: KtProperty,
            variableDescriptor: VariableDescriptorWithAccessors,
            scopeForDelegate: LexicalScope,
            trace: BindingTrace,
            dataFlowInfo: DataFlowInfo
    ): KotlinType {
        val traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property")
        val calleeExpression = delegateExpression.getCalleeExpressionIfAny()
        val completer = createConstraintSystemCompleter(property, variableDescriptor, delegateExpression, scopeForDelegate, trace, dataFlowInfo)

        calleeExpression?.let {
            traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, it, completer)
        }

        val delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE, dataFlowInfo, traceToResolveDelegatedProperty)

        traceToResolveDelegatedProperty.commit({ slice, _ -> slice !== CONSTRAINT_SYSTEM_COMPLETER }, true)

        return delegateType
    }

    private fun createConstraintSystemCompleter(
            property: KtProperty,
            variableDescriptor: VariableDescriptorWithAccessors,
            delegateExpression: KtExpression,
            scopeForDelegate: LexicalScope,
            trace: BindingTrace,
            dataFlowInfo: DataFlowInfo
    ): ConstraintSystemCompleter {
        val expectedType = if (property.typeReference != null) variableDescriptor.type else NO_EXPECTED_TYPE

        return object : ConstraintSystemCompleter {
            override fun completeConstraintSystem(constraintSystem: ConstraintSystem.Builder, resolvedCall: ResolvedCall<*>) {
                val returnType = resolvedCall.candidateDescriptor.returnType ?: return

                val typeVariableSubstitutor = constraintSystem.typeVariableSubstitutors[resolvedCall.call.toHandle()]
                                              ?: throw AssertionError("No substitutor in the system for call: " + resolvedCall.call)

                val traceToResolveConventionMethods = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods")

                val delegateType = getDelegateType(returnType, constraintSystem, typeVariableSubstitutor, traceToResolveConventionMethods)

                val getValueResults = getGetSetValueMethod(
                        variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, scopeForDelegate, dataFlowInfo,
                        isGet = true, isComplete = false
                )
                if (conventionMethodFound(getValueResults)) {
                    val getValueDescriptor = getValueResults.resultingDescriptor
                    val getValueReturnType = getValueDescriptor.returnType
                    if (getValueReturnType != null && !TypeUtils.noExpectedType(expectedType)) {
                        val returnTypeInSystem = typeVariableSubstitutor.substitute(getValueReturnType, Variance.INVARIANT)
                        if (returnTypeInSystem != null) {
                            constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position())
                        }
                    }
                    addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, getValueDescriptor)
                }
                if (!variableDescriptor.isVar) return

                // For the case: 'val v by d' (no declared type).
                // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
                // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well).
                if (variableDescriptor.returnType is DeferredType) return

                val setValueResults = getGetSetValueMethod(
                        variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, scopeForDelegate, dataFlowInfo,
                        isGet = false, isComplete = false
                )
                if (conventionMethodFound(setValueResults)) {
                    val setValueDescriptor = setValueResults.resultingDescriptor
                    val setValueParameters = setValueDescriptor.valueParameters
                    if (setValueParameters.size == 3) {
                        if (!noExpectedType(expectedType)) {
                            val thisParameterType = setValueParameters[2].type
                            val substitutedThisParameterType = typeVariableSubstitutor.substitute(thisParameterType, Variance.INVARIANT)
                            constraintSystem.addSubtypeConstraint(expectedType, substitutedThisParameterType, FROM_COMPLETER.position())
                        }
                        addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, setValueDescriptor)
                    }
                }
            }

            private fun getDelegateType(
                    byExpressionType: KotlinType,
                    constraintSystem: ConstraintSystem.Builder,
                    typeVariableSubstitutor: TypeSubstitutor,
                    traceToResolveConventionMethods: TemporaryBindingTrace
            ): KotlinType {
                if (isOperatorProvideDelegateSupported) {
                    val provideDelegateResults = getProvideDelegateMethod(
                            variableDescriptor, delegateExpression, byExpressionType,
                            traceToResolveConventionMethods, scopeForDelegate, dataFlowInfo
                    )
                    if (conventionMethodFound(provideDelegateResults)) {
                        val provideDelegateDescriptor = provideDelegateResults.resultingDescriptor
                        val provideDelegateReturnType = provideDelegateDescriptor.returnType
                        if (provideDelegateDescriptor.isOperator) {
                            addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, provideDelegateDescriptor,
                                                      dispatchReceiverOnly = true)
                            return provideDelegateReturnType
                                   ?: throw AssertionError("No return type fore 'provideDelegate' of ${delegateExpression.text}")
                        }
                    }
                }

                return byExpressionType
            }

            private fun conventionMethodFound(results: OverloadResolutionResults): Boolean =
                    results.isSuccess ||
                    results.isSingleResult && results.resultCode == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH

            private fun addConstraintForThisValue(
                    constraintSystem: ConstraintSystem.Builder,
                    typeVariableSubstitutor: TypeSubstitutor,
                    resultingDescriptor: FunctionDescriptor,
                    dispatchReceiverOnly: Boolean = false
            ) {
                val extensionReceiver = variableDescriptor.extensionReceiverParameter
                val dispatchReceiver = variableDescriptor.dispatchReceiverParameter
                val typeOfThis = (if (dispatchReceiverOnly) dispatchReceiver?.type else (extensionReceiver?.type ?: dispatchReceiver?.type))
                                 ?: builtIns.nullableNothingType

                val valueParameters = resultingDescriptor.valueParameters
                if (valueParameters.isEmpty()) return
                val valueParameterForThis = valueParameters[0]

                constraintSystem.addSubtypeConstraint(
                        typeOfThis,
                        typeVariableSubstitutor.substitute(valueParameterForThis.type, Variance.INVARIANT),
                        FROM_COMPLETER.position()
                )
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy