org.jetbrains.kotlin.resolve.OverloadResolver.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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.intellij.psi.PsiElement
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.reportOnDeclaration
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import java.util.*
@DefaultImplementation(impl = ConflictingOverloadsDispatcher.Default::class)
interface ConflictingOverloadsDispatcher {
fun getDiagnostic(
languageVersionSettings: LanguageVersionSettings,
declaration: DeclarationDescriptor,
redeclarations: Collection
): DiagnosticFactory1>?
object Default : ConflictingOverloadsDispatcher {
override fun getDiagnostic(
languageVersionSettings: LanguageVersionSettings,
declaration: DeclarationDescriptor,
redeclarations: Collection
): DiagnosticFactory1>? {
return when (declaration) {
is PropertyDescriptor, is ClassifierDescriptor -> Errors.REDECLARATION
is FunctionDescriptor -> Errors.CONFLICTING_OVERLOADS
else -> null
}
}
}
}
class OverloadResolver(
private val trace: BindingTrace,
private val overloadFilter: OverloadFilter,
private val overloadChecker: OverloadChecker,
private val errorDispatcher: ConflictingOverloadsDispatcher,
private val languageVersionSettings: LanguageVersionSettings,
mainFunctionDetectorFactory: MainFunctionDetector.Factory
) {
private val mainFunctionDetector = mainFunctionDetectorFactory.createMainFunctionDetector(trace, languageVersionSettings)
fun checkOverloads(c: BodiesResolveContext) {
val inClasses = findConstructorsInNestedClassesAndTypeAliases(c)
for (value in c.declaredClasses.values) {
checkOverloadsInClass(value, inClasses.get(value))
}
checkOverloadsInPackages(c)
}
private fun findConstructorsInNestedClassesAndTypeAliases(c: BodiesResolveContext): MultiMap {
val constructorsByOuterClass = MultiMap.create()
for (klass in c.declaredClasses.values) {
if (klass.kind.isSingleton || klass.name.isSpecial) {
// Constructors of singletons or anonymous object aren't callable from the code, so they shouldn't participate in overload name checking
continue
}
val containingDeclaration = klass.containingDeclaration
if (containingDeclaration is ScriptDescriptor) {
// TODO: check overload conflicts of functions with constructors in scripts
} else if (containingDeclaration is ClassDescriptor) {
constructorsByOuterClass.putValues(containingDeclaration, klass.constructors)
} else if (!(containingDeclaration is FunctionDescriptor ||
containingDeclaration is PropertyDescriptor ||
containingDeclaration is PackageFragmentDescriptor)) {
throw IllegalStateException("Illegal class container: " + containingDeclaration)
}
}
for (typeAlias in c.typeAliases.values) {
val containingDeclaration = typeAlias.containingDeclaration
if (containingDeclaration is ClassDescriptor) {
constructorsByOuterClass.putValues(containingDeclaration, typeAlias.constructors)
}
}
return constructorsByOuterClass
}
private fun checkOverloadsInPackages(c: BodiesResolveContext) {
val membersByName = groupModulePackageMembersByFqName(c, overloadFilter)
for (e in membersByName.entrySet()) {
checkOverloadsInPackage(e.value)
}
}
private fun groupModulePackageMembersByFqName(
c: BodiesResolveContext,
overloadFilter: OverloadFilter
): MultiMap {
val packageMembersByName = MultiMap()
collectModulePackageMembersWithSameName(
packageMembersByName,
(c.functions.values as Collection) + c.declaredClasses.values + c.typeAliases.values,
overloadFilter
) { scope, name ->
val functions = scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS)
val classifier = scope.getContributedClassifier(name, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS)
when (classifier) {
is ClassDescriptor ->
if (!classifier.kind.isSingleton)
functions + classifier.constructors
else
functions
is TypeAliasDescriptor ->
functions + classifier.constructors
else ->
functions
}
}
collectModulePackageMembersWithSameName(packageMembersByName, c.properties.values, overloadFilter) { scope, name ->
val variables = scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS)
val classifier = scope.getContributedClassifier(name, NoLookupLocation.WHEN_CHECK_DECLARATION_CONFLICTS)
variables + listOfNotNull(classifier)
}
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 checkOverloadsInClass(
classDescriptor: ClassDescriptorWithResolutionScopes,
nestedClassConstructors: Collection
) {
val functionsByName = MultiMap.create()
for (function in classDescriptor.declaredCallableMembers) {
functionsByName.putValue(function.name, function)
}
for (nestedConstructor in nestedClassConstructors) {
val name = nestedConstructor.containingDeclaration.name
functionsByName.putValue(name, nestedConstructor)
}
for (e in functionsByName.entrySet()) {
checkOverloadsInClass(e.value)
}
}
private fun checkOverloadsInPackage(members: Collection) {
if (members.size == 1) return
val redeclarationsMap = LinkedHashMap>()
for (redeclarationGroup in getPossibleRedeclarationGroups(members)) {
val redeclarations = findRedeclarations(redeclarationGroup)
redeclarations.forEach {
redeclarationsMap.getOrPut(it) { LinkedHashSet() }.addAll(redeclarations)
}
}
val reported = HashSet()
for ((member, conflicting) in redeclarationsMap) {
if (!reported.contains(member)) {
reported.addAll(conflicting)
reportRedeclarations(conflicting)
}
}
}
private fun getPossibleRedeclarationGroups(members: Collection): Collection> {
val result = arrayListOf>()
val nonPrivates = members.filter { !it.isPrivate() }
val bySourceFile = members.groupBy { DescriptorUtils.getContainingSourceFile(it) }
var hasGroupIncludingNonPrivateMembers = false
for (membersInFile in bySourceFile.values) {
// File member groups are interesting in redeclaration check if at least one file member is private.
if (membersInFile.any { it.isPrivate() }) {
hasGroupIncludingNonPrivateMembers = true
val group = LinkedHashSet(nonPrivates) + membersInFile
result.add(group)
}
}
if (!hasGroupIncludingNonPrivateMembers && nonPrivates.size > 1) {
result.add(nonPrivates)
}
return result
}
private fun DeclarationDescriptor.isPrivate() =
this is DeclarationDescriptorWithVisibility &&
DescriptorVisibilities.isPrivate(this.visibility)
private fun checkOverloadsInClass(members: Collection) {
if (members.size == 1) return
reportRedeclarations(findRedeclarations(members))
}
private fun DeclarationDescriptor.isSynthesized() =
this is CallableMemberDescriptor && kind == CallableMemberDescriptor.Kind.SYNTHESIZED
private fun findRedeclarations(members: Collection): Collection {
val redeclarations = linkedSetOf()
for (member1 in members) {
if (member1.isSynthesized()) continue
for (member2 in members) {
if (member1 == member2) continue
if (isConstructorsOfDifferentRedeclaredClasses(member1, member2)) continue
if (isTopLevelMainInDifferentFiles(member1, member2)) continue
if (isDefinitionsForDifferentPlatforms(member1, member2)) continue
if (isExpectDeclarationAndDefinition(member1, member2) || isExpectDeclarationAndDefinition(member2, member1)) continue
if (!overloadChecker.isOverloadable(member1, member2)) {
redeclarations.add(member1)
}
}
}
return redeclarations
}
private fun isConstructorsOfDifferentRedeclaredClasses(member1: DeclarationDescriptor, member2: DeclarationDescriptor): Boolean {
if (member1 !is ConstructorDescriptor || member2 !is ConstructorDescriptor) return false
// ignore conflicting overloads for constructors of different classes because their redeclarations will be reported
// but don't ignore if there's possibility that classes redeclarations will not be reported
// (e.g. they're declared in different packages)
val parent1 = member1.containingDeclaration
val parent2 = member2.containingDeclaration
return parent1 !== parent2 && parent1.containingDeclaration == parent2.containingDeclaration
}
private fun isTopLevelMainInDifferentFiles(member1: DeclarationDescriptor, member2: DeclarationDescriptor): Boolean {
if (!mainFunctionDetector.isMain(member1) || !mainFunctionDetector.isMain(member2)) {
return false
}
val file1 = DescriptorToSourceUtils.getContainingFile(member1)
val file2 = DescriptorToSourceUtils.getContainingFile(member2)
return file1 == null || file2 == null || file1 !== file2
}
private fun isExpectDeclarationAndDefinition(declaration: DeclarationDescriptor, definition: DeclarationDescriptor): Boolean {
return declaration is MemberDescriptor && declaration.isExpect &&
definition is MemberDescriptor && !definition.isExpect
}
private fun isDefinitionsForDifferentPlatforms(member1: DeclarationDescriptorNonRoot, member2: DeclarationDescriptorNonRoot): Boolean {
if (member1 !is MemberDescriptor || member2 !is MemberDescriptor) return false
return member1.isActual && member2.isActual &&
member1.platform != member2.platform
}
private fun reportRedeclarations(redeclarations: Collection) {
if (redeclarations.isEmpty()) return
for (memberDescriptor in redeclarations) {
val diagnostic = errorDispatcher.getDiagnostic(languageVersionSettings, memberDescriptor, redeclarations) ?: continue
reportOnDeclaration(trace, memberDescriptor) {
diagnostic.on(it, redeclarations)
}
}
}
}