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 d7106fb2a31..574c08d6496 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 @@ -37,3 +37,5 @@ annotation class MetaSupertype @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE, AnnotationTarget.EXPRESSION) @Retention(AnnotationRetention.SOURCE) annotation class MyComposable + +annotation class AllPropertiesConstructor 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 7853d17794d..9f1f008184d 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 @@ -32,6 +32,8 @@ class FirPluginPrototypeExtensionRegistrar : FirExtensionRegistrar() { +::AdditionalMembersGenerator +::CompanionGenerator +::MembersOfSerializerGenerator + + +::AllPropertiesConstructorMetadataProvider } } diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllPropertiesConstructorMetadataProvider.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllPropertiesConstructorMetadataProvider.kt new file mode 100644 index 00000000000..413e47891c7 --- /dev/null +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/AllPropertiesConstructorMetadataProvider.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2023 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.generators + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.extensions.FirDeclarationsForMetadataProviderExtension +import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.plugin.createConstructor +import org.jetbrains.kotlin.fir.plugin.fqn +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.scopes.getProperties +import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope + +class AllPropertiesConstructorMetadataProvider(session: FirSession) : FirDeclarationsForMetadataProviderExtension(session) { + companion object { + val ANNOTATION_FQN = "AllPropertiesConstructor".fqn() + val PREDICATE = DeclarationPredicate.create { annotated(ANNOTATION_FQN) } + } + + private object Key : GeneratedDeclarationKey() + + override fun provideDeclarationsForClass(klass: FirClass, scopeSession: ScopeSession): List { + if (!session.predicateBasedProvider.matches(PREDICATE, klass)) return emptyList() + val scope = klass.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false) + val properties = scope.getCallableNames().flatMap { scope.getProperties(it) } + val constructor = createConstructor(klass.symbol, Key) { + for (property in properties) { + valueParameter(property.name, property.resolvedReturnType) + } + } + return listOf(constructor) + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(PREDICATE) + } +} diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/AllPropertiesConstructorIrGenerator.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/AllPropertiesConstructorIrGenerator.kt new file mode 100644 index 00000000000..1546d7f8e75 --- /dev/null +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/AllPropertiesConstructorIrGenerator.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2023 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. + */ +@file:Suppress("warnings") +package org.jetbrains.kotlin.ir.plugin + +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.fir.plugin.generators.AllPropertiesConstructorMetadataProvider +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor +import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.types.isAny +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.name.JvmNames +import java.util.Comparator + +/* + * For classes annotated with @AllPropertiesConstructor and with no-arg constructor generates + * constructor which takes value parameters corresponding to all properties + * + * Parent class should be Any or class, annotated with @AllPropertiesConstructor + */ +class AllPropertiesConstructorIrGenerator(val context: IrPluginContext) : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + if (declaration.hasAnnotation()) { + declaration.declarations += getOrGenerateConstructorIfNeeded(declaration) + } + visitElement(declaration) + } + + private val generatedConstructors = mutableMapOf() + + private fun getOrGenerateConstructorIfNeeded(klass: IrClass): IrConstructor = generatedConstructors.getOrPut(klass) { + val superClass = klass.superTypes.mapNotNull(IrType::getClass).singleOrNull { it.kind == ClassKind.CLASS } ?: context.irBuiltIns.anyClass.owner + + val properties = klass.properties.toList().sortedWith(Comparator.comparing { if (it.origin == IrDeclarationOrigin.FAKE_OVERRIDE) 0 else 1 }) + val overriddenProperties = properties.takeWhile { it.origin == IrDeclarationOrigin.FAKE_OVERRIDE } + val superConstructor = when { + superClass.defaultType.isAny() -> superClass.constructors.singleOrNull { it.valueParameters.isEmpty() } + else -> { + require(superClass.hasAnnotation()) + superClass.constructors.singleOrNull { it.valueParameters.isNotEmpty() } + } + } ?: error("All properies constructor not found") + Unit + context.irFactory.buildConstructor { + startOffset = SYNTHETIC_OFFSET + endOffset = SYNTHETIC_OFFSET + returnType = klass.defaultType + }.also { ctor -> + ctor.parent = klass + ctor.valueParameters = properties.mapIndexed { index, property -> + buildValueParameter(ctor) { + this.index = index + type = property.getter!!.returnType + name = property.name + } + } + ctor.body = context.irFactory.createBlockBody( + ctor.startOffset, ctor.endOffset, + listOf( + IrDelegatingConstructorCallImpl( + ctor.startOffset, ctor.endOffset, context.irBuiltIns.unitType, + superConstructor.symbol, 0, superConstructor.valueParameters.size + ).apply { + ctor.valueParameters.take(overriddenProperties.size).forEachIndexed { index, parameter -> + putValueArgument( + index, + IrGetValueImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, parameter.symbol) + ) + } + } + ) + ) + } + } + + private fun IrClass.hasAnnotation(): Boolean { + return annotations.findAnnotation(AllPropertiesConstructorMetadataProvider.ANNOTATION_FQN) != null + } + + +} diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/GeneratedDeclarationsIrBodyFiller.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/GeneratedDeclarationsIrBodyFiller.kt index 8e86ed598a6..be27a0c2ab9 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/GeneratedDeclarationsIrBodyFiller.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/ir/plugin/GeneratedDeclarationsIrBodyFiller.kt @@ -17,6 +17,7 @@ class GeneratedDeclarationsIrBodyFiller : IrGenerationExtension { TransformerForCompanionGenerator(pluginContext), TransformerForAdditionalMembersGenerator(pluginContext), TransformerForTopLevelDeclarationsGenerator(pluginContext), + AllPropertiesConstructorIrGenerator(pluginContext) ) for (transformer in transformers) { @@ -24,4 +25,3 @@ class GeneratedDeclarationsIrBodyFiller : IrGenerationExtension { } } } - diff --git a/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.ir.txt b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.ir.txt new file mode 100644 index 00000000000..968e2975621 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.ir.txt @@ -0,0 +1,277 @@ +Module: a +FILE fqName: fileName:/module_a_classWithAllPropertiesConstructor.kt + CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.A + CONSTRUCTOR visibility:public <> (s:kotlin.String) returnType:.A [primary] + VALUE_PARAMETER name:s index:0 type:kotlin.String + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:FINAL visibility:public superTypes:[kotlin.Any]' + PROPERTY name:s visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final] + EXPRESSION_BODY + GET_VAR 's: kotlin.String declared in .A.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.A) returnType:kotlin.String + correspondingProperty: PROPERTY name:s visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.A + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in .A' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.String origin=null + receiver: GET_VAR ': .A declared in .A.' type=.A origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:B modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.B + CONSTRUCTOR visibility:public <> (s:kotlin.String) returnType:.B [primary] + VALUE_PARAMETER name:s index:0 type:kotlin.String + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:B modality:FINAL visibility:public superTypes:[kotlin.Any]' + PROPERTY name:s visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final] + EXPRESSION_BODY + GET_VAR 's: kotlin.String declared in .B.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.B) returnType:kotlin.String + correspondingProperty: PROPERTY name:s visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.B + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in .B' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.String origin=null + receiver: GET_VAR ': .B declared in .B.' type=.B origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:C modality:FINAL visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.C + CONSTRUCTOR visibility:public <> (s:kotlin.String) returnType:.C [primary] + VALUE_PARAMETER name:s index:0 type:kotlin.String + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:FINAL visibility:public superTypes:[kotlin.Any]' + PROPERTY name:s visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final] + EXPRESSION_BODY + GET_VAR 's: kotlin.String declared in .C.' type=kotlin.String origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.C) returnType:kotlin.String + correspondingProperty: PROPERTY name:s visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.C + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): kotlin.String declared in .C' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String visibility:private [final]' type=kotlin.String origin=null + receiver: GET_VAR ': .C declared in .C.' type=.C origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:Base modality:OPEN visibility:public superTypes:[kotlin.Any] + annotations: + AllPropertiesConstructor + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Base + CONSTRUCTOR visibility:public <> () returnType:.Base [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:Base modality:OPEN visibility:public superTypes:[kotlin.Any]' + PROPERTY name:a visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:a type:.A visibility:private [final] + EXPRESSION_BODY + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .A' type=.A origin=null + s: CONST String type=kotlin.String value="a" + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.Base) returnType:.A + correspondingProperty: PROPERTY name:a visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.Base + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): .A declared in .Base' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:a type:.A visibility:private [final]' type=.A origin=null + receiver: GET_VAR ': .Base declared in .Base.' type=.Base origin=null + PROPERTY name:b visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:b type:.B visibility:private [final] + EXPRESSION_BODY + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .B' type=.B origin=null + s: CONST String type=kotlin.String value="b" + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.Base) returnType:.B + correspondingProperty: PROPERTY name:b visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.Base + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): .B declared in .Base' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:b type:.B visibility:private [final]' type=.B origin=null + receiver: GET_VAR ': .Base declared in .Base.' type=.Base origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CONSTRUCTOR visibility:public <> (a:.A, b:.B) returnType:.Base + VALUE_PARAMETER name:a index:0 type:.A + VALUE_PARAMETER name:b index:1 type:.B + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' +Module: b +FILE fqName: fileName:/Derived.kt + CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[.Base] + annotations: + AllPropertiesConstructor + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Derived + CONSTRUCTOR visibility:public <> () returnType:.Derived [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in .Base' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[.Base]' + PROPERTY name:c visibility:public modality:FINAL [val] + FIELD PROPERTY_BACKING_FIELD name:c type:.C visibility:private [final] + EXPRESSION_BODY + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .C' type=.C origin=null + s: CONST String type=kotlin.String value="c" + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.Derived) returnType:.C + correspondingProperty: PROPERTY name:c visibility:public modality:FINAL [val] + $this: VALUE_PARAMETER name: type:.Derived + BLOCK_BODY + RETURN type=kotlin.Nothing from='public final fun (): .C declared in .Derived' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:c type:.C visibility:private [final]' type=.C origin=null + receiver: GET_VAR ': .Derived declared in .Derived.' type=.Derived origin=null + PROPERTY FAKE_OVERRIDE name:a visibility:public modality:FINAL [fake_override,val] + overridden: + public final a: .A [val] + FUN FAKE_OVERRIDE name: visibility:public modality:FINAL <> ($this:.Base) returnType:.A [fake_override] + correspondingProperty: PROPERTY FAKE_OVERRIDE name:a visibility:public modality:FINAL [fake_override,val] + overridden: + public final fun (): .A declared in .Base + $this: VALUE_PARAMETER name: type:.Base + PROPERTY FAKE_OVERRIDE name:b visibility:public modality:FINAL [fake_override,val] + overridden: + public final b: .B [val] + FUN FAKE_OVERRIDE name: visibility:public modality:FINAL <> ($this:.Base) returnType:.B [fake_override] + correspondingProperty: PROPERTY FAKE_OVERRIDE name:b visibility:public modality:FINAL [fake_override,val] + overridden: + public final fun (): .B declared in .Base + $this: VALUE_PARAMETER name: type:.Base + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [fake_override,operator] declared in .Base + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int [fake_override] declared in .Base + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String [fake_override] declared in .Base + $this: VALUE_PARAMETER name: type:kotlin.Any + CONSTRUCTOR visibility:public <> (a:.A, b:.B, c:.C) returnType:.Derived + VALUE_PARAMETER name:a index:0 type:.A + VALUE_PARAMETER name:b index:1 type:.B + VALUE_PARAMETER name:c index:2 type:.C + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor (a: .A, b: .B) declared in .Base' + a: GET_VAR 'a: .A declared in .Derived.' type=.A origin=null + b: GET_VAR 'b: .B declared in .Derived.' type=.B origin=null +FILE fqName: fileName:/main.kt + FUN name:box visibility:public modality:FINAL <> () returnType:kotlin.String + BLOCK_BODY + VAR name:constructor type:java.lang.reflect.Constructor<.Derived> [val] + CALL 'public final fun CHECK_NOT_NULL (arg0: T0 of kotlin.internal.ir.CHECK_NOT_NULL?): {T0 of kotlin.internal.ir.CHECK_NOT_NULL & Any} declared in kotlin.internal.ir' type=java.lang.reflect.Constructor<.Derived> origin=EXCLEXCL + : java.lang.reflect.Constructor<.Derived> + arg0: CALL 'public final fun (): java.lang.reflect.Constructor>? declared in kotlin.reflect.jvm.ReflectJvmMapping' type=java.lang.reflect.Constructor<.Derived>? origin=GET_PROPERTY + : .Derived + $receiver: CALL 'public final fun first (predicate: kotlin.Function1): T of kotlin.collections.CollectionsKt.first [inline] declared in kotlin.collections.CollectionsKt' type=kotlin.reflect.KFunction<.Derived> origin=null + : kotlin.reflect.KFunction<.Derived> + $receiver: CALL 'public abstract fun (): kotlin.collections.Collection> declared in kotlin.reflect.KClass' type=kotlin.collections.Collection.Derived>> origin=GET_PROPERTY + $this: CLASS_REFERENCE 'CLASS CLASS name:Derived modality:FINAL visibility:public superTypes:[.Base]' type=kotlin.reflect.KClass<.Derived> + predicate: FUN_EXPR type=kotlin.Function1.Derived>, kotlin.Boolean> origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> (it:kotlin.reflect.KFunction<.Derived>) returnType:kotlin.Boolean + VALUE_PARAMETER name:it index:0 type:kotlin.reflect.KFunction<.Derived> + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (it: kotlin.reflect.KFunction<.Derived>): kotlin.Boolean declared in .box' + CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'public abstract fun (): kotlin.Int declared in kotlin.collections.List' type=kotlin.Int origin=GET_PROPERTY + $this: CALL 'public final fun (): kotlin.collections.List declared in kotlin.reflect.full.KCallables' type=kotlin.collections.List origin=GET_PROPERTY + $receiver: GET_VAR 'it: kotlin.reflect.KFunction<.Derived> declared in .box.' type=kotlin.reflect.KFunction<.Derived> origin=null + arg1: CONST Int type=kotlin.Int value=3 + VAR name:derived type:@[FlexibleNullability] .Derived? [val] + CALL 'public open fun newInstance (vararg p0: @[FlexibleNullability] kotlin.Any?): @[FlexibleNullability] T of java.lang.reflect.Constructor? declared in java.lang.reflect.Constructor' type=@[FlexibleNullability] .Derived? origin=null + $this: GET_VAR 'val constructor: java.lang.reflect.Constructor<.Derived> [val] declared in .box' type=java.lang.reflect.Constructor<.Derived> origin=null + p0: VARARG type=@[FlexibleNullability] kotlin.Array? varargElementType=@[FlexibleNullability] kotlin.Any? + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .A' type=.A origin=null + s: CONST String type=kotlin.String value="a" + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .B' type=.B origin=null + s: CONST String type=kotlin.String value="b" + CONSTRUCTOR_CALL 'public constructor (s: kotlin.String) [primary] declared in .C' type=.C origin=null + s: CONST String type=kotlin.String value="c" + RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in ' + CALL 'public final fun with (receiver: T of kotlin.StandardKt.with, block: @[ExtensionFunctionType] kotlin.Function1): R of kotlin.StandardKt.with [inline] declared in kotlin.StandardKt' type=kotlin.String origin=null + : @[FlexibleNullability] .Derived? + : kotlin.String + receiver: GET_VAR 'val derived: @[FlexibleNullability] .Derived? [val] declared in .box' type=@[FlexibleNullability] .Derived? origin=null + block: FUN_EXPR type=@[ExtensionFunctionType] kotlin.Function1<@[FlexibleNullability] .Derived?, kotlin.String> origin=LAMBDA + FUN LOCAL_FUNCTION_FOR_LAMBDA name: visibility:local modality:FINAL <> ($receiver:@[FlexibleNullability] .Derived?) returnType:kotlin.String + $receiver: VALUE_PARAMETER name:$this$with type:@[FlexibleNullability] .Derived? + BLOCK_BODY + RETURN type=kotlin.Nothing from='local final fun (): kotlin.String declared in .box' + WHEN type=kotlin.String origin=IF + BRANCH + if: WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: WHEN type=kotlin.Boolean origin=ANDAND + BRANCH + if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'public final fun (): kotlin.String declared in .A' type=kotlin.String origin=GET_PROPERTY + $this: CALL 'public final fun (): .A [fake_override] declared in .Derived' type=.A origin=GET_PROPERTY + $this: GET_VAR '$this$with: @[FlexibleNullability] .Derived? declared in .box.' type=@[FlexibleNullability] .Derived? origin=null + arg1: CONST String type=kotlin.String value="a" + then: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'public final fun (): kotlin.String declared in .B' type=kotlin.String origin=GET_PROPERTY + $this: CALL 'public final fun (): .B [fake_override] declared in .Derived' type=.B origin=GET_PROPERTY + $this: GET_VAR '$this$with: @[FlexibleNullability] .Derived? declared in .box.' type=@[FlexibleNullability] .Derived? origin=null + arg1: CONST String type=kotlin.String value="b" + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + then: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ + arg0: CALL 'public final fun (): kotlin.String declared in .C' type=kotlin.String origin=GET_PROPERTY + $this: CALL 'public final fun (): .C declared in .Derived' type=.C origin=GET_PROPERTY + $this: GET_VAR '$this$with: @[FlexibleNullability] .Derived? declared in .box.' type=@[FlexibleNullability] .Derived? origin=null + arg1: CONST String type=kotlin.String value="c" + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Boolean type=kotlin.Boolean value=false + then: CONST String type=kotlin.String value="OK" + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST String type=kotlin.String value="Error" diff --git a/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.txt b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.txt new file mode 100644 index 00000000000..3914f66b9a4 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.fir.txt @@ -0,0 +1,72 @@ +Module: a +FILE: module_a_classWithAllPropertiesConstructor.kt + public final class A : R|kotlin/Any| { + public constructor(s: R|kotlin/String|): R|A| { + super() + } + + public final val s: R|kotlin/String| = R|/s| + public get(): R|kotlin/String| + + } + public final class B : R|kotlin/Any| { + public constructor(s: R|kotlin/String|): R|B| { + super() + } + + public final val s: R|kotlin/String| = R|/s| + public get(): R|kotlin/String| + + } + public final class C : R|kotlin/Any| { + public constructor(s: R|kotlin/String|): R|C| { + super() + } + + public final val s: R|kotlin/String| = R|/s| + public get(): R|kotlin/String| + + } + @R|org/jetbrains/kotlin/fir/plugin/AllPropertiesConstructor|() public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + public final val a: R|A| = R|/A.A|(String(a)) + public get(): R|A| + + public final val b: R|B| = R|/B.B|(String(b)) + public get(): R|B| + + } +Module: b +FILE: Derived.kt + @R|org/jetbrains/kotlin/fir/plugin/AllPropertiesConstructor|() public final class Derived : R|Base| { + public constructor(): R|Derived| { + super() + } + + public final val c: R|C| = R|/C.C|(String(c)) + public get(): R|C| + + } +FILE: main.kt + public final fun box(): R|kotlin/String| { + lval constructor: R|java/lang/reflect/Constructor| = (Q|Derived|).R|SubstitutionOverride>|>|.R|kotlin/collections/first||>( = first@fun (it: R|kotlin/reflect/KFunction|): R|kotlin/Boolean| { + ^ ==(R|/it|.R|kotlin/reflect/full/valueParameters|.R|SubstitutionOverride|, Int(3)) + } + ).R|kotlin/reflect/jvm/javaConstructor|!! + lval derived: R|Derived!| = R|/constructor|.R|SubstitutionOverride|(vararg(R|/A.A|(String(a)), R|/B.B|(String(b)), R|/C.C|(String(c)))) + ^box R|kotlin/with|(R|/derived|, = with@fun R|Derived!|.(): R|kotlin/String| { + ^ when () { + ==(this@R|special/anonymous|.R|/Base.a|.R|/A.s|, String(a)) && ==(this@R|special/anonymous|.R|/Base.b|.R|/B.s|, String(b)) && ==(this@R|special/anonymous|.R|/Derived.c|.R|/C.s|, String(c)) -> { + String(OK) + } + else -> { + String(Error) + } + } + + } + ) + } diff --git a/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.kt b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.kt new file mode 100644 index 00000000000..0cd11bad913 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.kt @@ -0,0 +1,43 @@ +// FIR_DISABLE_LAZY_RESOLVE_CHECKS +// DUMP_IR +// WITH_STDLIB +// WITH_REFLECT +// FULL_JDK + +// MODULE: a +import org.jetbrains.kotlin.fir.plugin.AllPropertiesConstructor + +class A(val s: String) +class B(val s: String) +class C(val s: String) + +@AllPropertiesConstructor +open class Base { + val a: A = A("a") + val b = B("b") +} + +// MODULE: b(a) +// FILE: Derived.kt +import org.jetbrains.kotlin.fir.plugin.AllPropertiesConstructor + +@AllPropertiesConstructor +class Derived : Base() { + val c = C("c") +} + +// FILE: main.kt +import kotlin.reflect.full.valueParameters +import kotlin.reflect.jvm.javaConstructor + +fun box(): String { + val constructor = Derived::class.constructors.first { it.valueParameters.size == 3 }.javaConstructor!! + val derived = constructor.newInstance(A("a"), B("b"), C("c")) + return with (derived) { + if (a.s == "a" && b.s == "b" && c.s == "c") { + "OK" + } else { + "Error" + } + } +} diff --git a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java index 77e99dc1f07..96141894daf 100644 --- a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java +++ b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java @@ -25,6 +25,12 @@ public class FirPluginBlackBoxCodegenTestGenerated extends AbstractFirPluginBlac KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir-plugin-prototype/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("classWithAllPropertiesConstructor.kt") + public void testClassWithAllPropertiesConstructor() throws Exception { + runTest("plugins/fir-plugin-prototype/testData/box/classWithAllPropertiesConstructor.kt"); + } + @Test @TestMetadata("classWithCompanionObject.kt") public void testClassWithCompanionObject() throws Exception {