Decrease resolution priority of declarations from kotlin-stdlib-jre7/8

In order to allow "overload resolution ambiguity" when both
kotlin-stdlib-jre7/8 and kotlin-stdlib-jdk7/8 are in the dependencies
This commit is contained in:
Alexander Udalov
2017-09-09 14:59:41 +03:00
committed by Ilya Gorbunov
parent 1592555783
commit a4f378d04d
8 changed files with 117 additions and 4 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.USE_NEW_INFERENCE
import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
import org.jetbrains.kotlin.resolve.calls.util.isLowPriorityFromStdlibJre7Or8
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.hasHidesMembersAnnotation
@@ -59,7 +60,9 @@ internal abstract class AbstractScopeTowerLevel(
diagnostics.add(ErrorDescriptorDiagnostic)
}
else {
if (descriptor.hasLowPriorityInOverloadResolution()) diagnostics.add(LowPriorityDescriptorDiagnostic)
if (descriptor.hasLowPriorityInOverloadResolution() || descriptor.isLowPriorityFromStdlibJre7Or8()) {
diagnostics.add(LowPriorityDescriptorDiagnostic)
}
if (dispatchReceiverSmartCastType != null) diagnostics.add(UsedSmartCastForDispatchReceiver(dispatchReceiverSmartCastType))
if (!USE_NEW_INFERENCE) {
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2017 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.util
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.SINCE_KOTLIN_FQ_NAME
private val kotlin: FqName = KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
private val kotlinText: FqName = KotlinBuiltIns.TEXT_PACKAGE_FQ_NAME
private val kotlinCollections: FqName = KotlinBuiltIns.COLLECTIONS_PACKAGE_FQ_NAME
private val kotlinStreams: FqName = kotlin.child(Name.identifier("streams"))
private val use: Name = Name.identifier("use")
private val get: Name = Name.identifier("get")
private val getOrDefault: Name = Name.identifier("getOrDefault")
private val remove: Name = Name.identifier("remove")
// We treat all declarations in deprecated libraries kotlin-stdlib-jre7 and kotlin-stdlib-jre8 as if they were
// annotated with @LowPriorityInOverloadResolution.
// This is needed in order to avoid reporting "overload resolution ambiguity" in case when there's both kotlin-stdlib-jreN and
// kotlin-stdlib-jdkN in dependencies, to allow smooth migration from the former to the latter. The ambiguity here would be incorrect
// because there's really no ambiguity in the bytecode since all declarations in -jdk7/8 have been moved to another JVM package.
// We locate declarations in kotlin-stdlib-jre{7,8} by FQ name and @SinceKotlin annotation value, which should be 1.1.
fun CallableDescriptor.isLowPriorityFromStdlibJre7Or8(): Boolean {
val containingPackage = containingDeclaration as? PackageFragmentDescriptor ?: return false
val packageFqName = containingPackage.fqName
if (!packageFqName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return false
val isFromStdlibJre7Or8 =
packageFqName == kotlin && name == use ||
packageFqName == kotlinText && name == get ||
packageFqName == kotlinCollections && (name == getOrDefault || name == remove) ||
packageFqName == kotlinStreams
if (!isFromStdlibJre7Or8) return false
val sinceKotlin = annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME) ?: return false
val version = sinceKotlin.allValueArguments.values.singleOrNull()?.value as? String ?: return false
return version == LanguageVersion.KOTLIN_1_1.versionString
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2017 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 org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin")
/**
* @return true if the descriptor is accessible according to [languageVersionSettings], or false otherwise. The [actionIfInaccessible]
* callback is called with the version specified in the [SinceKotlin] annotation if the descriptor is inaccessible.
*/
fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(
languageVersionSettings: LanguageVersionSettings,
actionIfInaccessible: ((ApiVersion) -> Unit)? = null
): Boolean {
val version =
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
else getOwnSinceKotlinVersion()
// Allow access in the following cases:
// 1) There's no @SinceKotlin annotation for this descriptor
// 2) There's a @SinceKotlin annotation but its value is some unrecognizable nonsense
// 3) The value as a version is not greater than our API version
if (version == null || version <= languageVersionSettings.apiVersion) return true
actionIfInaccessible?.invoke(version)
return false
}
/**
* @return null if there are no overridden members or if there's at least one declaration in the hierarchy not annotated with [SinceKotlin],
* or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise.
*/
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): ApiVersion? {
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }.min()
}
private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): ApiVersion? {
// TODO: use-site targeted annotations
fun DeclarationDescriptor.loadAnnotationValue(): ApiVersion? =
(annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)?.allValueArguments?.values?.singleOrNull()?.value as? String)
?.let(ApiVersion.Companion::parse)
val ownVersion = loadAnnotationValue()
val ctorClass = (this as? ConstructorDescriptor)?.containingDeclaration?.loadAnnotationValue()
val property = (this as? PropertyAccessorDescriptor)?.correspondingProperty?.loadAnnotationValue()
val typeAliasDescriptor = (this as? TypeAliasDescriptor) ?:
(this as? TypeAliasConstructorDescriptor)?.typeAliasDescriptor ?:
(this as? FakeCallableDescriptorForTypeAliasObject)?.typeAliasDescriptor
val typeAlias = typeAliasDescriptor?.loadAnnotationValue()
// We should check only the upper-most classifier ('A' in 'A<B<C>>') to guarantee binary compatibility.
val underlyingClass = typeAliasDescriptor?.classDescriptor?.loadAnnotationValue()
val underlyingConstructor = (this as? TypeAliasConstructorDescriptor)?.underlyingConstructorDescriptor?.loadAnnotationValue()
val underlyingObject = (this as? FakeCallableDescriptorForTypeAliasObject)?.getReferencedObject()?.loadAnnotationValue()
return listOfNotNull(ownVersion, ctorClass, property, typeAlias, underlyingClass, underlyingConstructor, underlyingObject).max()
}