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

org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 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.calls.smartcasts

import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST
import org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeIntersector
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import java.util.*

class SmartCastManager {

    fun getSmartCastVariants(
            receiverToCast: ReceiverValue,
            context: ResolutionContext<*>
    ): List {
        return getSmartCastVariants(receiverToCast, context.trace.bindingContext, context.scope.ownerDescriptor, context.dataFlowInfo)
    }

    fun getSmartCastVariants(
            receiverToCast: ReceiverValue,
            bindingContext: BindingContext,
            containingDeclarationOrModule: DeclarationDescriptor,
            dataFlowInfo: DataFlowInfo
    ): List {
        val variants = getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast)
        val result = ArrayList(variants.size + 1)
        result.add(receiverToCast.type)
        result.addAll(variants)
        return result
    }

    /**
     * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
     */
    fun getSmartCastVariantsExcludingReceiver(
            context: ResolutionContext<*>,
            receiverToCast: ReceiverValue
    ): Collection {
        return getSmartCastVariantsExcludingReceiver(context.trace.bindingContext,
                                                     context.scope.ownerDescriptor,
                                                     context.dataFlowInfo,
                                                     receiverToCast)
    }

    /**
     * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
     */
    fun getSmartCastVariantsExcludingReceiver(
            bindingContext: BindingContext,
            containingDeclarationOrModule: DeclarationDescriptor,
            dataFlowInfo: DataFlowInfo,
            receiverToCast: ReceiverValue
    ): Collection {
        val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverToCast, bindingContext, containingDeclarationOrModule)
        return dataFlowInfo.getCollectedTypes(dataFlowValue)
    }

    fun isSubTypeBySmartCastIgnoringNullability(
            receiverArgument: ReceiverValue,
            receiverParameterType: KotlinType,
            context: ResolutionContext<*>
    ): Boolean {
        val smartCastTypes = getSmartCastVariants(receiverArgument, context)
        return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null
    }

    private fun getSmartCastSubType(
            receiverParameterType: KotlinType,
            smartCastTypes: Collection
    ): KotlinType? {
        val subTypes = smartCastTypes
                .filter { ArgumentTypeResolver.isSubtypeOfForArgumentType(it, receiverParameterType) }
                .distinct()
        if (subTypes.isEmpty()) return null

        val intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes)
        if (intersection == null || !intersection.constructor.isDenotable) {
            return receiverParameterType
        }
        return intersection
    }

    companion object {

        private fun recordCastOrError(
                expression: KtExpression,
                type: KotlinType,
                trace: BindingTrace,
                dataFlowValue: DataFlowValue,
                recordExpressionType: Boolean
        ) {
            if (KotlinBuiltIns.isNullableNothing(type)) return
            if (dataFlowValue.isPredictable) {
                trace.record(SMARTCAST, expression, type)
                if (recordExpressionType) {
                    //TODO
                    //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
                    trace.recordType(expression, type)
                }
            }
            else {
                trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.text, dataFlowValue.kind.description))
            }
        }

        fun checkAndRecordPossibleCast(
                dataFlowValue: DataFlowValue,
                expectedType: KotlinType,
                expression: KtExpression?,
                c: ResolutionContext<*>,
                calleeExpression: KtExpression?,
                recordExpressionType: Boolean
        ): SmartCastResult? {
            return checkAndRecordPossibleCast(dataFlowValue, expectedType, null, expression, c, calleeExpression, recordExpressionType)
        }

        fun checkAndRecordPossibleCast(
                dataFlowValue: DataFlowValue,
                expectedType: KotlinType,
                additionalPredicate: ((KotlinType) -> Boolean)?,
                expression: KtExpression?,
                c: ResolutionContext<*>,
                calleeExpression: KtExpression?,
                recordExpressionType: Boolean
        ): SmartCastResult? {
            for (possibleType in c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType) && (additionalPredicate == null || additionalPredicate(possibleType))) {
                    if (expression != null) {
                        recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType)
                    }
                    else if (calleeExpression != null && dataFlowValue.isPredictable) {
                        c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType)
                    }
                    return SmartCastResult(possibleType, dataFlowValue.isPredictable)
                }
            }

            if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable) {
                // Handling cases like:
                // fun bar(x: Any) {}
                // fun  foo(x: T) {
                //      if (x != null) {
                //          bar(x) // Should be allowed with smart cast
                //      }
                // }
                //
                // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
                // But at the same time we're sure that `x` can't be null and just check for such cases manually

                // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
                // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
                val immanentlyNotNull = !dataFlowValue.immanentNullability.canBeNull()
                val nullableExpectedType = TypeUtils.makeNullable(expectedType)

                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.type, nullableExpectedType) && (additionalPredicate == null || additionalPredicate(dataFlowValue.type))) {
                    if (!immanentlyNotNull && expression != null) {
                        recordCastOrError(expression, dataFlowValue.type, c.trace, dataFlowValue, recordExpressionType)
                    }

                    return SmartCastResult(dataFlowValue.type, immanentlyNotNull || dataFlowValue.isPredictable)
                }
                return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType)
            }

            return null
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy