Improve 1.0-compatibility mode for JDK dependent members

Just reporting error by call checker may be too restrictive
in case there are some extensions that can be used successfully

Solution is to annotate additional members with
@Deprecated(level=Error) + @kotlin.internal.LowPriorityInOverloadResolution
This commit is contained in:
Denis Zharkov
2016-09-21 16:31:04 +03:00
parent a7dedfab70
commit 5381c06936
10 changed files with 86 additions and 33 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.frontend.java.di
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.LazyResolveToken
@@ -140,7 +141,8 @@ fun StorageComponentContainer.javaAnalysisInit() {
// 'initBuiltIns' body cannot be merged with 'javaAnalysisInit' because the latter is used in IDE,
// where built-ins instances are shared between modules and manual initialization is necessary (to avoid multiple initialization)
fun StorageComponentContainer.initJvmBuiltInsForTopDownAnalysis() {
get<JvmBuiltIns>().initialize(get<ModuleDescriptor>())
get<JvmBuiltIns>().initialize(get<ModuleDescriptor>(),
get<LanguageVersionSettings>().supportsFeature(LanguageFeature.AdditionalBuiltInsMembers))
}
class ContainerForTopDownAnalyzerForJvm(container: StorageComponentContainer) {
@@ -29,20 +29,8 @@ import org.jetbrains.kotlin.load.java.isFromBuiltins
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DeclarationChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
object AdditionalBuiltInsMembersCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers)) return
val resultingDescriptor = resolvedCall.resultingDescriptor as? CallableMemberDescriptor ?: return
reportErrorIfAdditionalBuiltinDescriptor(resultingDescriptor, context.trace, reportOn)
}
}
object AdditionalBuiltInsMemberOverrideDeclarationChecker : DeclarationChecker {
override fun check(
declaration: KtDeclaration,
@@ -54,8 +54,7 @@ object JvmPlatformConfigurator : PlatformConfigurator(
UnsupportedSyntheticCallableReferenceChecker(),
SuperCallWithDefaultArgumentsChecker(),
MissingDependencyClassChecker(),
ProtectedSyntheticExtensionCallChecker,
AdditionalBuiltInsMembersCallChecker
ProtectedSyntheticExtensionCallChecker
),
additionalTypeCheckers = listOf(
@@ -1,18 +1,27 @@
// !CHECK_TYPE
// !LANGUAGE: -AdditionalBuiltInsMembers
// SKIP_TXT
class A : java.util.ArrayList<String>() {
<!UNSUPPORTED_FEATURE!>override<!> fun stream(): java.util.stream.Stream<String> = super.<!UNSUPPORTED_FEATURE!>stream<!>()
<!UNSUPPORTED_FEATURE!>override<!> fun stream(): java.util.stream.Stream<String> = super.<!DEPRECATION_ERROR!>stream<!>()
}
class A1 : java.util.ArrayList<String>() {
// TODO: should be allowed
<!VIRTUAL_MEMBER_HIDDEN!>fun stream(): java.util.stream.Stream<String><!> = super.<!UNSUPPORTED_FEATURE!>stream<!>()
<!VIRTUAL_MEMBER_HIDDEN!>fun stream(): java.util.stream.Stream<String><!> = super.<!DEPRECATION_ERROR!>stream<!>()
}
class B : <!UNSUPPORTED_FEATURE!>Throwable<!>("", null, false, false)
class B : <!DEPRECATION_ERROR!>Throwable<!>("", null, false, false)
fun foo(x: List<String>) {
x.<!UNSUPPORTED_FEATURE!>stream<!>()
java.util.ArrayList<String>().<!UNSUPPORTED_FEATURE!>stream<!>()
fun Throwable.fillInStackTrace() = 1
fun foo(x: List<String>, y: Throwable) {
x.<!DEPRECATION_ERROR!>stream<!>()
java.util.ArrayList<String>().<!DEPRECATION_ERROR!>stream<!>()
y.fillInStackTrace() checkType { _<Int>() }
// TODO: Relevant extensions has been removed from stdlib
// We should consider reverting their removal
y.<!DEPRECATION_ERROR!>printStackTrace<!>()
}
@@ -26,6 +26,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -77,7 +80,11 @@ class MultiModuleJavaAnalysisCustomTest : KtUsefulTestCase() {
packagePartProviderFactory = { a, b -> JvmPackagePartProvider(environment) }
)
builtIns.initialize(resolverForProject.descriptorForModule(resolverForProject.allModules.first()))
builtIns.initialize(
resolverForProject.descriptorForModule(resolverForProject.allModules.first()),
resolverForProject.resolverForModule(resolverForProject.allModules.first())
.componentProvider.get<LanguageVersionSettings>()
.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers))
performChecks(resolverForProject, modules)
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.builtins.BuiltInsInitializer
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl
import org.jetbrains.kotlin.descriptors.annotations.createDeprecatedAnnotation
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.platform.createMappedTypeParametersSubstitution
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
@@ -47,17 +49,20 @@ import org.jetbrains.kotlin.types.LazyWrappedType
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import java.io.Serializable
import java.util.*
open class JvmBuiltInsSettings(
private val moduleDescriptor: ModuleDescriptor,
storageManager: StorageManager,
deferredOwnerModuleDescriptor: () -> ModuleDescriptor
deferredOwnerModuleDescriptor: () -> ModuleDescriptor,
isAdditionalBuiltInsFeatureSupported: () -> Boolean
) : AdditionalClassPartsProvider, PlatformDependentDeclarationFilter {
private val j2kClassMap = JavaToKotlinClassMap.INSTANCE
private val ownerModuleDescriptor: ModuleDescriptor by lazy(deferredOwnerModuleDescriptor)
private val isAdditionalBuiltInsFeatureSupported: Boolean by lazy(isAdditionalBuiltInsFeatureSupported)
private val mockSerializableType = storageManager.createMockJavaIoSerializableType()
@@ -70,6 +75,28 @@ open class JvmBuiltInsSettings(
).let { AnnotationsImpl(listOf(it)) }
}
private val notSupportedDeprecation by storageManager.createLazyValue {
// We use both LowPriorityInOverloadResolution to achieve the following goals:
// - If there is something to resolve to beside an additional built-in member, it's *almost* always will win
// - Otherwise error will be reported because of Deprecated annotation with Error level
val lowPriorityAnnotation =
ClassDescriptorImpl(
moduleDescriptor.getPackage(LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME.parent()),
LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME.shortName(), Modality.FINAL, ClassKind.ANNOTATION_CLASS,
moduleDescriptor.builtIns.anyType.singletonList(), SourceElement.NO_SOURCE
).run {
initialize(MemberScope.Empty, emptySet(), null)
AnnotationDescriptorImpl(defaultType, emptyMap(), SourceElement.NO_SOURCE)
}
val errorDeprecation = moduleDescriptor.builtIns.createDeprecatedAnnotation(
"This member is not supported by Kotlin compiler on this language level",
level = DeprecationLevel.ERROR.name
)
AnnotationsImpl(listOf(lowPriorityAnnotation, errorDeprecation))
}
private fun StorageManager.createMockJavaIoSerializableType(): KotlinType {
val mockJavaIoPackageFragment = object : PackageFragmentDescriptorImpl(moduleDescriptor, FqName("java.io")) {
override fun getMemberScope() = MemberScope.Empty
@@ -120,12 +147,21 @@ open class JvmBuiltInsSettings(
}
JDKMemberStatus.NOT_CONSIDERED -> {
setAdditionalAnnotations(notConsideredDeprecation)
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
else {
setAdditionalAnnotations(notConsideredDeprecation)
}
}
JDKMemberStatus.DROP -> return@mapNotNull null
JDKMemberStatus.WHITE_LIST -> {} // Do nothing
JDKMemberStatus.WHITE_LIST -> {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
}
}
}.build()!!
@@ -260,7 +296,10 @@ open class JvmBuiltInsSettings(
setPreserveSourceElement()
setSubstitution(substitutor.substitution)
if (SignatureBuildingComponents.signature(javaAnalogueDescriptor, javaConstructor.computeJvmDescriptor()) !in WHITE_LIST_CONSTRUCTOR_SIGNATURES) {
if (!isAdditionalBuiltInsFeatureSupported) {
setAdditionalAnnotations(notSupportedDeprecation)
}
else if (SignatureBuildingComponents.signature(javaAnalogueDescriptor, javaConstructor.computeJvmDescriptor()) !in WHITE_LIST_CONSTRUCTOR_SIGNATURES) {
setAdditionalAnnotations(notConsideredDeprecation)
}
@@ -26,10 +26,12 @@ import org.jetbrains.kotlin.utils.sure
class JvmBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageManager) {
// Module containing JDK classes or having them among dependencies
private var ownerModuleDescriptor: ModuleDescriptor? = null
private var isAdditionalBuiltInsFeatureSupported: Boolean = true
fun initialize(moduleDescriptor: ModuleDescriptor) {
fun initialize(moduleDescriptor: ModuleDescriptor, isAdditionalBuiltInsFeatureSupported: Boolean) {
assert(ownerModuleDescriptor == null) { "JvmBuiltins repeated initialization" }
this.ownerModuleDescriptor = moduleDescriptor
this.isAdditionalBuiltInsFeatureSupported = isAdditionalBuiltInsFeatureSupported
}
lateinit var settings: JvmBuiltInsSettings
@@ -39,7 +41,8 @@ class JvmBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageManage
override fun getPlatformDependentDeclarationFilter(): PlatformDependentDeclarationFilter {
settings = JvmBuiltInsSettings(
builtInsModule, storageManager,
{ ownerModuleDescriptor.sure { "JvmBuiltins has not been initialized properly" } }
{ ownerModuleDescriptor.sure { "JvmBuiltins has not been initialized properly" } },
{ isAdditionalBuiltInsFeatureSupported }
)
return settings
@@ -63,7 +63,7 @@ class RuntimeModuleData private constructor(val deserialization: Deserialization
val lazyJavaPackageFragmentProvider = LazyJavaPackageFragmentProvider(globalJavaResolverContext)
builtIns.initialize(module)
builtIns.initialize(module, isAdditionalBuiltInsFeatureSupported = true)
val javaDescriptorResolver = JavaDescriptorResolver(lazyJavaPackageFragmentProvider, javaResolverCache)
val javaClassDataFinder = JavaClassDataFinder(reflectKotlinClassFinder, deserializedDescriptorResolver)
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.types.KotlinType
private val NO_INFER_ANNOTATION_FQ_NAME = FqName("kotlin.internal.NoInfer")
private val EXACT_ANNOTATION_FQ_NAME = FqName("kotlin.internal.Exact")
private val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.internal.LowPriorityInOverloadResolution")
val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.internal.LowPriorityInOverloadResolution")
private val HIDES_MEMBERS_ANNOTATION_FQ_NAME = FqName("kotlin.internal.HidesMembers")
private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes")
@@ -65,4 +65,4 @@ private class AnnotationsWithOnly(val presentAnnotation: FqName): Annotations {
override fun getUseSiteTargetedAnnotations(): List<AnnotationWithTarget> = emptyList()
override fun getAllAnnotations(): List<AnnotationWithTarget> = emptyList()
}
}
@@ -32,6 +32,9 @@ import org.jetbrains.kotlin.analyzer.EmptyResolverForProject
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.context.GlobalContext
import org.jetbrains.kotlin.context.GlobalContextImpl
@@ -381,9 +384,12 @@ private fun globalResolveSessionProvider(
if (newBuiltIns is JvmBuiltIns) {
val sdkInfo = SdkInfo(project, sdk!!)
newBuiltIns.initialize(moduleResolverProvider.resolverForProject.descriptorForModule(sdkInfo))
newBuiltIns.initialize(
moduleResolverProvider.resolverForProject.descriptorForModule(sdkInfo),
moduleResolverProvider.resolverForProject.resolverForModule(sdkInfo)
.componentProvider.get<LanguageVersionSettings>()
.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers))
}
moduleResolverProvider
}