diff --git a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt index 7f9c61f4341..ef4e56162f6 100644 --- a/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt +++ b/compiler/serialization/src/org/jetbrains/kotlin/serialization/DescriptorSerializer.kt @@ -19,6 +19,8 @@ package org.jetbrains.kotlin.serialization import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.getFunctionalClassKind +import org.jetbrains.kotlin.builtins.isSuspendFunctionType +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.name.Name @@ -28,8 +30,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.resolve.constants.NullValue import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.contains import org.jetbrains.kotlin.utils.Interner import java.io.ByteArrayOutputStream import java.util.* @@ -214,6 +217,10 @@ class DescriptorSerializer private constructor( } } + if (descriptor.isSuspendOrHasSuspendTypesInSignature()) { + builder.sinceKotlinInfo = writeSinceKotlinInfo(LanguageFeature.Coroutines) + } + extension.serializeProperty(descriptor, builder) return builder @@ -266,6 +273,10 @@ class DescriptorSerializer private constructor( } } + if (descriptor.isSuspendOrHasSuspendTypesInSignature()) { + builder.sinceKotlinInfo = writeSinceKotlinInfo(LanguageFeature.Coroutines) + } + extension.serializeFunction(descriptor, builder) return builder @@ -285,11 +296,25 @@ class DescriptorSerializer private constructor( builder.addValueParameter(local.valueParameter(valueParameterDescriptor)) } + if (descriptor.isSuspendOrHasSuspendTypesInSignature()) { + builder.sinceKotlinInfo = writeSinceKotlinInfo(LanguageFeature.Coroutines) + } + extension.serializeConstructor(descriptor, builder) return builder } + private fun CallableMemberDescriptor.isSuspendOrHasSuspendTypesInSignature(): Boolean { + if (this is FunctionDescriptor && isSuspend) return true + + return listOfNotNull( + extensionReceiverParameter?.type, + returnType, + *valueParameters.map(ValueParameterDescriptor::getType).toTypedArray() + ).any { type -> type.contains(UnwrappedType::isSuspendFunctionType) } + } + fun typeAliasProto(descriptor: TypeAliasDescriptor): ProtoBuf.TypeAlias.Builder { val builder = ProtoBuf.TypeAlias.newBuilder() val local = createChildSerializer(descriptor) @@ -549,6 +574,17 @@ class DescriptorSerializer private constructor( return builder } + private fun writeSinceKotlinInfo(languageFeature: LanguageFeature): Int { + val languageVersion = languageFeature.sinceVersion!! + val sinceKotlinInfo = ProtoBuf.SinceKotlinInfo.newBuilder().apply { + SinceKotlinInfo.Version(languageVersion.major, languageVersion.minor).encode( + writeVersion = { version = it }, + writeVersionFull = { versionFull = it } + ) + } + return sinceKotlinInfoTable[sinceKotlinInfo] + } + private fun getClassifierId(descriptor: ClassifierDescriptorWithTypeParameters): Int = stringTable.getFqNameIndex(descriptor) diff --git a/compiler/testData/sinceKotlinInfo/suspendFun.kt b/compiler/testData/sinceKotlinInfo/suspendFun.kt new file mode 100644 index 00000000000..6e3777d81b7 --- /dev/null +++ b/compiler/testData/sinceKotlinInfo/suspendFun.kt @@ -0,0 +1,16 @@ +package test + +suspend fun topLevel() {} + +class Foo { + constructor(block: suspend () -> Unit) + + suspend fun member() {} +} + +fun async1(block: suspend () -> Unit) {} +fun (suspend () -> Unit).async2() {} +fun async3(): suspend () -> Unit = null!! +fun async4(): Map Unit>? = null + +val (suspend () -> Unit).asyncVal: () -> Unit get() = {} diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/SinceKotlinInfoTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/SinceKotlinInfoTest.kt new file mode 100644 index 00000000000..48d8d8fd46b --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/serialization/SinceKotlinInfoTest.kt @@ -0,0 +1,113 @@ +/* + * 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.serialization + +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.jvm.compiler.LoadDescriptorUtil +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor +import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestCaseWithTmpdir +import org.jetbrains.kotlin.test.TestJdkKind +import java.io.File + +class SinceKotlinInfoTest : TestCaseWithTmpdir() { + fun doTest( + fileName: String, + expectedSinceKotlinInfoVersion: SinceKotlinInfo.Version, + expectedLevel: DeprecationLevel, + expectedMessage: String?, + expectedErrorCode: Int?, + vararg fqNames: String + ) { + LoadDescriptorUtil.compileKotlinToDirAndGetModule( + listOf(File(fileName)), tmpdir, + KotlinTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(testRootDisposable) + ) + + val (_, module) = JvmResolveUtil.analyze( + KotlinCoreEnvironment.createForTests( + testRootDisposable, + KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, tmpdir), + EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + ) + + fun check(descriptor: DeclarationDescriptor) { + if (descriptor !is DeserializedMemberDescriptor) { + throw AssertionError("Not a deserialized descriptor: $descriptor") + } + + val sinceKotlinInfo = descriptor.sinceKotlinInfo ?: throw AssertionError("No SinceKotlinInfo for $descriptor") + + assertEquals(expectedSinceKotlinInfoVersion, sinceKotlinInfo.version) + assertEquals(expectedLevel, sinceKotlinInfo.level) + assertEquals(expectedMessage, sinceKotlinInfo.message) + assertEquals(expectedErrorCode, sinceKotlinInfo.errorCode) + } + + for (fqName in fqNames) { + check(module.findUnambiguousDescriptorByFqName(fqName)) + } + } + + private fun ModuleDescriptor.findUnambiguousDescriptorByFqName(fqName: String): DeclarationDescriptor { + val names = fqName.split('.') + var descriptor: DeclarationDescriptor = getPackage(FqName(names.first())) + for (name in names.drop(1)) { + val descriptors = when (name) { + "" -> (descriptor as ClassDescriptor).constructors + else -> { + val scope = when (descriptor) { + is PackageViewDescriptor -> descriptor.memberScope + is ClassDescriptor -> descriptor.unsubstitutedMemberScope + else -> error("Unsupported: $descriptor") + } + scope.getDescriptorsFiltered(nameFilter = { it.asString() == name }) + } + } + if (descriptors.isEmpty()) throw AssertionError("Descriptor not found: $name in $descriptor") + descriptor = descriptors.singleOrNull() ?: throw AssertionError("Not a unambiguous descriptor: $name in $descriptor") + } + return descriptor + } + + fun testSuspendFun() { + doTest("compiler/testData/sinceKotlinInfo/suspendFun.kt", + SinceKotlinInfo.Version(1, 1), DeprecationLevel.ERROR, null, null, + "test.topLevel", + "test.Foo.member", + "test.Foo.", + "test.async1", + "test.async2", + "test.async3", + "test.async4", + "test.asyncVal" + ) + } +} diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 1e95aef1a57..4a04b108528 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -58,9 +58,12 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) { } } -enum class LanguageVersion(val versionString: String) : DescriptionAware { - KOTLIN_1_0("1.0"), - KOTLIN_1_1("1.1"); +enum class LanguageVersion(val major: Int, val minor: Int) : DescriptionAware { + KOTLIN_1_0(1, 0), + KOTLIN_1_1(1, 1); + + val versionString: String + get() = "$major.$minor" override val description: String get() = versionString