From 7ce3934cf7001388557758a976ba902044aff240 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 23 Sep 2022 12:14:17 +0300 Subject: [PATCH] [FIR] Add type resolution service into SerializationFirSupertypesExtension This service allows resolve custom user types from plugins, which allows to support specifying type arguments of generated supertypes basing on arguments passed to annotations --- build.gradle.kts | 2 + .../FirSupertypeGenerationExtension.kt | 10 +++ .../transformers/FirSupertypesResolution.kt | 28 +++++-- .../kotlin/fir/plugin/annotations.kt | 4 + .../FirPluginPrototypeExtensionRegistrar.kt | 1 + .../SomeAdditionalSupertypeGenerator.kt | 2 + .../plugin/SupertypeWithArgumentGenerator.kt | 78 +++++++++++++++++++ .../supertypes/supertypeWithArgument.fir.txt | 75 ++++++++++++++++++ .../supertypes/supertypeWithArgument.kt | 43 ++++++++++ .../FirPluginDiagnosticTestGenerated.java | 6 ++ .../SerializationFirSupertypesExtension.kt | 4 +- 11 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SupertypeWithArgumentGenerator.kt create mode 100644 plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.fir.txt create mode 100644 plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.kt diff --git a/build.gradle.kts b/build.gradle.kts index 1e4cb14a2b8..466172ac8b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -371,9 +371,11 @@ val projectsWithEnabledContextReceivers by extra { listOf( ":core:descriptors.jvm", ":compiler:frontend.common", + ":compiler:fir:resolve", ":compiler:fir:fir2ir", ":kotlin-lombok-compiler-plugin.k1", ":kotlinx-serialization-compiler-plugin.k2", + ":plugins:fir-plugin-prototype" ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension.kt index 637f02d1d7c..c1b154d6720 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.extensions import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.FirUserTypeRef import kotlin.reflect.KClass abstract class FirSupertypeGenerationExtension(session: FirSession) : FirExtension(session) { @@ -22,12 +23,21 @@ abstract class FirSupertypeGenerationExtension(session: FirSession) : FirExtensi abstract fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean + context(TypeResolveServiceContainer) + @Suppress("IncorrectFormatting") // KTIJ-22227 abstract fun computeAdditionalSupertypes( classLikeDeclaration: FirClassLikeDeclaration, resolvedSupertypes: List ): List fun interface Factory : FirExtension.Factory + + class TypeResolveServiceContainer(val typeResolver: TypeResolveService) + + abstract class TypeResolveService { + abstract fun resolveUserType(type: FirUserTypeRef): FirResolvedTypeRef + } } val FirExtensionService.supertypeGenerators: List by FirExtensionService.registeredExtensions() + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypesResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypesResolution.kt index 5b9b1b6200a..073aa2cf60f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypesResolution.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypesResolution.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension import org.jetbrains.kotlin.fir.extensions.extensionService import org.jetbrains.kotlin.fir.extensions.supertypeGenerators import org.jetbrains.kotlin.fir.resolve.* @@ -421,22 +422,39 @@ open class FirSupertypeResolverVisitor( superTypeRef } }.also { - addSupertypesFromExtensions(classLikeDeclaration, it) + addSupertypesFromExtensions(classLikeDeclaration, it, transformer, scopeDeclaration) } } } private fun List.createCopy(): List = ArrayList(this) - private fun addSupertypesFromExtensions(klass: FirClassLikeDeclaration, supertypeRefs: MutableList) { + private fun addSupertypesFromExtensions( + klass: FirClassLikeDeclaration, + supertypeRefs: MutableList, + typeResolveTransformer: FirTransformer, + scopeDeclaration: ScopeClassDeclaration + ) { if (supertypeGenerationExtensions.isEmpty()) return - for (extension in supertypeGenerationExtensions) { - if (extension.needTransformSupertypes(klass)) { - supertypeRefs += extension.computeAdditionalSupertypes(klass, supertypeRefs) + val typeResolveService = TypeResolveServiceForPlugins(typeResolveTransformer, scopeDeclaration) + with(FirSupertypeGenerationExtension.TypeResolveServiceContainer(typeResolveService)) { + for (extension in supertypeGenerationExtensions) { + if (extension.needTransformSupertypes(klass)) { + supertypeRefs += extension.computeAdditionalSupertypes(klass, supertypeRefs) + } } } } + private class TypeResolveServiceForPlugins( + val typeResolveTransformer: FirTransformer, + val scopeDeclaration: ScopeClassDeclaration + ) : FirSupertypeGenerationExtension.TypeResolveService() { + override fun resolveUserType(type: FirUserTypeRef): FirResolvedTypeRef { + return type.transform(typeResolveTransformer, scopeDeclaration) + } + } + override fun visitTypeAlias(typeAlias: FirTypeAlias, data: Any?) { // TODO: this if is a temporary hack for built-in types (because we can't load file for them) if (typeAlias.expandedTypeRef is FirResolvedTypeRef) { diff --git a/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt b/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt index d30914a77ff..bcc4696b93d 100644 --- a/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt +++ b/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.fir.plugin +import kotlin.reflect.KClass + annotation class AllOpen annotation class DummyFunction @@ -27,3 +29,5 @@ annotation class Negative enum class Visibility { Public, Internal, Private, Protected } + +annotation class SupertypeWithTypeArgument(val kClass: KClass<*>) diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirPluginPrototypeExtensionRegistrar.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirPluginPrototypeExtensionRegistrar.kt index 0c08a41ad5b..7ac71ce36be 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirPluginPrototypeExtensionRegistrar.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirPluginPrototypeExtensionRegistrar.kt @@ -19,6 +19,7 @@ class FirPluginPrototypeExtensionRegistrar : FirExtensionRegistrar() { +::AllOpenStatusTransformer +::AllPublicVisibilityTransformer +::SomeAdditionalSupertypeGenerator + +::SupertypeWithArgumentGenerator +::PluginAdditionalCheckers +::FirNumberSignAttributeExtension +::AlgebraReceiverInjector diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SomeAdditionalSupertypeGenerator.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SomeAdditionalSupertypeGenerator.kt index e769e548be7..532c600d64a 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SomeAdditionalSupertypeGenerator.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SomeAdditionalSupertypeGenerator.kt @@ -30,6 +30,8 @@ class SomeAdditionalSupertypeGenerator(session: FirSession) : FirSupertypeGenera } + context(TypeResolveServiceContainer) + @Suppress("IncorrectFormatting") // KTIJ-22227 override fun computeAdditionalSupertypes( classLikeDeclaration: FirClassLikeDeclaration, resolvedSupertypes: List diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SupertypeWithArgumentGenerator.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SupertypeWithArgumentGenerator.kt new file mode 100644 index 00000000000..d98710f8b99 --- /dev/null +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/SupertypeWithArgumentGenerator.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.extensions.buildUserTypeFromQualifierParts +import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate +import org.jetbrains.kotlin.fir.extensions.predicate.annotated +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.constructClassLikeType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +/* + * Adds MyInterface supertype for all classes annotated with @MyInterfaceSupertype + */ +class SupertypeWithArgumentGenerator(session: FirSession) : FirSupertypeGenerationExtension(session) { + companion object { + private val supertypeClassId = ClassId(FqName("foo"), Name.identifier("InterfaceWithArgument")) + private val annotationClassId = ClassId.topLevel("SupertypeWithTypeArgument".fqn()) + private val PREDICATE: DeclarationPredicate = annotated(annotationClassId.asSingleFqName()) + + } + + context(TypeResolveServiceContainer) + @Suppress("IncorrectFormatting") // KTIJ-22227 + override fun computeAdditionalSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List + ): List { + if (resolvedSupertypes.any { it.type.classId == supertypeClassId }) return emptyList() + + val annotation = classLikeDeclaration.getAnnotationByClassId(annotationClassId) ?: return emptyList() + val getClassArgument = (annotation as? FirAnnotationCall)?.argument as? FirGetClassCall ?: return emptyList() + + val typeToResolve = buildUserTypeFromQualifierParts(isMarkedNullable = false) { + fun visitQualifiers(expression: FirExpression) { + if (expression !is FirPropertyAccessExpression) return + expression.explicitReceiver?.let { visitQualifiers(it) } + expression.qualifierName?.let { part(it) } + } + visitQualifiers(getClassArgument.argument) + } + + val resolvedArgument = typeResolver.resolveUserType(typeToResolve).type + + return listOf( + buildResolvedTypeRef { + type = supertypeClassId.constructClassLikeType(arrayOf(resolvedArgument), isNullable = false) + } + ) + } + + private val FirPropertyAccessExpression.qualifierName: Name? + get() = (calleeReference as? FirSimpleNamedReference)?.name + + override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean { + return session.predicateBasedProvider.matches(PREDICATE, declaration) + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(PREDICATE) + } +} diff --git a/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.fir.txt b/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.fir.txt new file mode 100644 index 00000000000..cf6413d314c --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.fir.txt @@ -0,0 +1,75 @@ +FILE: supertypeWithArgument.kt + package foo + + public abstract interface InterfaceWithArgument : R|kotlin/Any| { + public open fun generate(): R|T| { + ^generate Null(null)!! + } + + } + @R|org/jetbrains/kotlin/fir/plugin/SupertypeWithTypeArgument|(kClass = (Q|kotlin/String|)) public final class TopLevelType : R|kotlin/Any|, R|foo/InterfaceWithArgument| { + public constructor(): R|foo/TopLevelType| { + super() + } + + } + public final class Nested : R|kotlin/Any| { + public constructor(): R|foo/Nested| { + super() + } + + } + public abstract class Base : R|kotlin/Any| { + public constructor(): R|foo/Base| { + super() + } + + public final class Nested : R|kotlin/Any| { + public constructor(): R|foo/Base.Nested| { + super() + } + + } + + } + public final class Derived : R|foo/Base| { + public constructor(): R|foo/Derived| { + super() + } + + @R|org/jetbrains/kotlin/fir/plugin/SupertypeWithTypeArgument|(kClass = (Q|foo/Base.Nested|)) public final class TypeFromSupertype : R|kotlin/Any|, R|foo/InterfaceWithArgument| { + public constructor(): R|foo/Derived.TypeFromSupertype| { + super() + } + + } + + @R|org/jetbrains/kotlin/fir/plugin/SupertypeWithTypeArgument|(kClass = (Q|foo/Nested|)) public final class QualifiedType : R|kotlin/Any|, R|foo/InterfaceWithArgument| { + public constructor(): R|foo/Derived.QualifiedType| { + super() + } + + } + + } + @R|org/jetbrains/kotlin/fir/plugin/SupertypeWithTypeArgument|(kClass = (#)) public final class UnresolvedType : R|kotlin/Any|, R|foo/InterfaceWithArgument| { + public constructor(): R|foo/UnresolvedType| { + super() + } + + } + public final fun takeString(x: R|kotlin/String|): R|kotlin/Unit| { + } + public final fun takeTopLevelNested(x: R|foo/Base.Nested|): R|kotlin/Unit| { + } + public final fun takeInnerNested(x: R|foo/Nested|): R|kotlin/Unit| { + } + public final fun test_1(x: R|foo/TopLevelType|): R|kotlin/Unit| { + R|foo/takeString|(R|/x|.R|SubstitutionOverride|()) + } + public final fun test_2(x: R|foo/Derived.TypeFromSupertype|): R|kotlin/Unit| { + R|foo/takeTopLevelNested|(R|/x|.R|SubstitutionOverride|()) + } + public final fun test_3(x: R|foo/Derived.QualifiedType|): R|kotlin/Unit| { + R|foo/takeInnerNested|(R|/x|.R|SubstitutionOverride|()) + } diff --git a/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.kt b/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.kt new file mode 100644 index 00000000000..1899d2665ca --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.kt @@ -0,0 +1,43 @@ +package foo + +import org.jetbrains.kotlin.fir.plugin.SupertypeWithTypeArgument + +interface InterfaceWithArgument { + fun generate(): T = null!! +} + +@SupertypeWithTypeArgument(String::class) +class TopLevelType + +class Nested + +abstract class Base { + class Nested +} + +class Derived : Base() { + @SupertypeWithTypeArgument(Nested::class) + class TypeFromSupertype + + @SupertypeWithTypeArgument(foo.Nested::class) + class QualifiedType +} + +@SupertypeWithTypeArgument(UnresolvedClass::class) +class UnresolvedType + +fun takeString(x: String) {} +fun takeTopLevelNested(x: Base.Nested) {} +fun takeInnerNested(x: Nested) {} + +fun test_1(x: TopLevelType) { + takeString(x.generate()) +} + +fun test_2(x: Derived.TypeFromSupertype) { + takeTopLevelNested(x.generate()) +} + +fun test_3(x: Derived.QualifiedType) { + takeInnerNested(x.generate()) +} diff --git a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginDiagnosticTestGenerated.java b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginDiagnosticTestGenerated.java index ae99d0f17e4..4c43219100a 100644 --- a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginDiagnosticTestGenerated.java +++ b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginDiagnosticTestGenerated.java @@ -138,5 +138,11 @@ public class FirPluginDiagnosticTestGenerated extends AbstractFirPluginDiagnosti public void testSimple() throws Exception { runTest("plugins/fir-plugin-prototype/testData/diagnostics/supertypes/simple.kt"); } + + @Test + @TestMetadata("supertypeWithArgument.kt") + public void testSupertypeWithArgument() throws Exception { + runTest("plugins/fir-plugin-prototype/testData/diagnostics/supertypes/supertypeWithArgument.kt"); + } } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt index 273e68e59d3..66223cab318 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt @@ -26,6 +26,8 @@ class SerializationFirSupertypesExtension(session: FirSession) : FirSupertypeGen register(serializerFor) } + context(TypeResolveServiceContainer) + @Suppress("IncorrectFormatting") // KTIJ-22227 override fun computeAdditionalSupertypes( classLikeDeclaration: FirClassLikeDeclaration, resolvedSupertypes: List @@ -39,4 +41,4 @@ class SerializationFirSupertypesExtension(session: FirSession) : FirSupertypeGen TODO("Support @Serializer(for=...) supertype generation") } else emptyList() } -} \ No newline at end of file +}