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

org.jetbrains.kotlin.resolve.OverloadUtil.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

import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.INCOMPATIBLE
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeIntersector
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.oneMoreSpecificThanAnother
import org.jetbrains.kotlin.utils.singletonOrEmptyList

object OverloadUtil {

    /**
     * Does not check names.
     */
    @JvmStatic fun isOverloadable(a: DeclarationDescriptor, b: DeclarationDescriptor): Boolean {
        val aCategory = getDeclarationCategory(a)
        val bCategory = getDeclarationCategory(b)

        if (aCategory != bCategory) return true
        if (a !is CallableDescriptor || b !is CallableDescriptor) return false

        OverridingUtil.checkReceiverAndParameterCount(a, b)?.let { return it.result == INCOMPATIBLE }

        val aValueParameters = OverridingUtil.compiledValueParameters(a)
        val bValueParameters = OverridingUtil.compiledValueParameters(b)

        for ((aType, bType) in aValueParameters.zip(bValueParameters)) {
            // TODO: check type parameters, create a substitution and compare parameter types according to it, like in OverridingUtil
            val superValueParameterType = aType.upperBound
            val subValueParameterType = bType.upperBound
            if (!KotlinTypeChecker.DEFAULT.equalTypes(superValueParameterType, subValueParameterType) ||
                oneMoreSpecificThanAnother(subValueParameterType, superValueParameterType)) {
                return true
            }
        }

        return false
    }

    private enum class DeclarationCategory {
        TYPE_OR_VALUE,
        FUNCTION,
        EXTENSION_PROPERTY
    }

    private fun DeclarationDescriptor.isExtensionProperty() =
            this is PropertyDescriptor &&
            extensionReceiverParameter != null

    private fun getDeclarationCategory(a: DeclarationDescriptor): DeclarationCategory =
            when (a) {
                is PropertyDescriptor ->
                    if (a.isExtensionProperty())
                        DeclarationCategory.EXTENSION_PROPERTY
                    else
                        DeclarationCategory.TYPE_OR_VALUE
                is ConstructorDescriptor,
                is SimpleFunctionDescriptor ->
                    DeclarationCategory.FUNCTION
                is ClassifierDescriptor ->
                    DeclarationCategory.TYPE_OR_VALUE
                else ->
                    error("Unexpected declaration kind: $a")
            }

    private val KotlinType.upperBound: KotlinType
        get() {
            val classifier = constructor.declarationDescriptor
            return when (classifier) {
                is ClassDescriptor -> this
                is TypeParameterDescriptor -> TypeIntersector.getUpperBoundsAsType(classifier)
                else -> error("Unknown type constructor: $this")
            }
        }

    @JvmStatic fun groupModulePackageMembersByFqName(
            c: BodiesResolveContext,
            overloadFilter: OverloadFilter
    ): MultiMap {
        val packageMembersByName = MultiMap()

        collectModulePackageMembersWithSameName(packageMembersByName, c.functions.values + c.declaredClasses.values, overloadFilter) {
            scope, name ->
            val functions = scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_REDECLARATIONS)
            val classifier = scope.getContributedClassifier(name, NoLookupLocation.WHEN_CHECK_REDECLARATIONS)
            if (classifier is ClassDescriptor && !classifier.kind.isSingleton)
                functions + classifier.constructors
            else
                functions
        }

        collectModulePackageMembersWithSameName(packageMembersByName, c.properties.values, overloadFilter) {
            scope, name ->
            val variables = scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_REDECLARATIONS)
            val classifier = scope.getContributedClassifier(name, NoLookupLocation.WHEN_CHECK_REDECLARATIONS)
            variables + classifier.singletonOrEmptyList()
        }

        return packageMembersByName
    }

    private inline fun collectModulePackageMembersWithSameName(
            packageMembersByName: MultiMap,
            interestingDescriptors: Collection,
            overloadFilter: OverloadFilter,
            getMembersByName: (MemberScope, Name) -> Collection
    ) {
        val observedFQNs = hashSetOf()
        for (descriptor in interestingDescriptors) {
            if (descriptor.containingDeclaration !is PackageFragmentDescriptor) continue

            val descriptorFQN = DescriptorUtils.getFqName(descriptor)
            if (observedFQNs.contains(descriptorFQN)) continue
            observedFQNs.add(descriptorFQN)

            val packageMembersWithSameName = getModulePackageMembersWithSameName(descriptor, overloadFilter, getMembersByName)
            packageMembersByName.putValues(descriptorFQN, packageMembersWithSameName)
        }
    }

    private inline fun getModulePackageMembersWithSameName(
            descriptor: DeclarationDescriptor,
            overloadFilter: OverloadFilter,
            getMembersByName: (MemberScope, Name) -> Collection
    ): Collection {
        val containingPackage = descriptor.containingDeclaration
        if (containingPackage !is PackageFragmentDescriptor) {
            throw AssertionError("$descriptor is not a top-level package member")
        }

        val containingModule = DescriptorUtils.getContainingModuleOrNull(descriptor) ?:
                               return when (descriptor) {
                                   is CallableMemberDescriptor -> listOf(descriptor)
                                   is ClassDescriptor -> descriptor.constructors
                                   else -> throw AssertionError("Unexpected descriptor kind: $descriptor")
                               }

        val containingPackageScope = containingModule.getPackage(containingPackage.fqName).memberScope
        val possibleOverloads =
                getMembersByName(containingPackageScope, descriptor.name).filter {
                    // NB memberScope for PackageViewDescriptor includes module dependencies
                    DescriptorUtils.getContainingModule(it) == containingModule
                }

        return overloadFilter.filterPackageMemberOverloads(possibleOverloads)
    }

    private fun DeclarationDescriptor.isPrivate() =
            this is DeclarationDescriptorWithVisibility &&
            Visibilities.isPrivate(this.visibility)

    @JvmStatic fun getPossibleRedeclarationGroups(
            members: Collection
    ): Collection> {
        val result = arrayListOf>()

        val nonPrivates = members.filter { !it.isPrivate() }
        if (nonPrivates.size > 1) {
            result.add(nonPrivates)
        }

        val bySourceFile = MultiMap.createSmart()
        for (member in members) {
            val sourceFile = DescriptorUtils.getContainingSourceFile(member)
            if (sourceFile != SourceFile.NO_SOURCE_FILE) {
                bySourceFile.putValue(sourceFile, member)
            }
        }

        for ((sourceFile, membersInFile) in bySourceFile.entrySet()) {
            // File member groups are interesting in redeclaration check if at least one file member is private.
            if (membersInFile.size > 1 && membersInFile.any { it.isPrivate() }) {
                result.add(membersInFile)
            }
        }

        return result
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy