diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java index ef10cc79c8f..23aa6870513 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyBodiesCalculatorTestGenerated.java @@ -39,6 +39,18 @@ public class FirOutOfContentRootLazyBodiesCalculatorTestGenerated extends Abstra runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @Test + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @Test + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @Test @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java index 0fa86774e18..0c3120fa2d7 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyBodiesCalculatorTestGenerated.java @@ -39,6 +39,18 @@ public class FirSourceLazyBodiesCalculatorTestGenerated extends AbstractFirSourc runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @Test + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @Test + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @Test @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java index a571f381a1f..2db4988c98f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java @@ -6747,6 +6747,12 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/falseSamConversion.kt"); } + @Test + @TestMetadata("FieldAnnotationWithClasses.kt") + public void testFieldAnnotationWithClasses() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt"); + } + @Test @TestMetadata("immutableName.kt") public void testImmutableName() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java index dd8def67877..dded6fdef43 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java @@ -6747,6 +6747,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/falseSamConversion.kt"); } + @Test + @TestMetadata("FieldAnnotationWithClasses.kt") + public void testFieldAnnotationWithClasses() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt"); + } + @Test @TestMetadata("immutableName.kt") public void testImmutableName() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/concurrent.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/concurrent.fir.txt index 3ede227b95e..be8e2582b8f 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/concurrent.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/concurrent.fir.txt @@ -1,5 +1,5 @@ FILE: concurrent.kt - @R|kotlin/jvm/Volatile|() public final var xx: R|kotlin/Int| = Int(2) + field:@R|kotlin/jvm/Volatile|() public final var xx: R|kotlin/Int| = Int(2) public get(): R|kotlin/Int| public set(value: R|kotlin/Int|): R|kotlin/Unit| @R|kotlin/jvm/Synchronized|() public final fun foo(): R|kotlin/Unit| { diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.fir.txt new file mode 100644 index 00000000000..4dcc7f093e1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.fir.txt @@ -0,0 +1,34 @@ +FILE: FieldAnnotationWithClasses.kt + public final annotation class Ann : R|kotlin/Annotation| { + public constructor(vararg allowedTypes: R|kotlin/Array>|): R|Ann| { + super() + } + + public final val allowedTypes: R|kotlin/Array>| = R|/allowedTypes| + public get(): R|kotlin/Array>| + + } + public final fun foo(): R|kotlin/Unit| { + local final class Local : R|kotlin/Any| { + public constructor(): R|Local| { + super() + } + + field:@FIELD:R|Ann|(allowedTypes = vararg(allowedTypes = ((Q|Some|), (Q|Other|)))) public final val x: R|kotlin/Int| = Int(42) + public get(): R|kotlin/Int| + + } + + } + public final class Some : R|kotlin/Any| { + public constructor(): R|Some| { + super() + } + + } + public final class Other : R|kotlin/Any| { + public constructor(): R|Other| { + super() + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt new file mode 100644 index 00000000000..be58ad8f1bf --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt @@ -0,0 +1,15 @@ +// WITH_REFLECT + +import kotlin.reflect.* + +annotation class Ann(vararg val allowedTypes: KClass<*>) + +fun foo() { + class Local { + @field:Ann(allowedTypes = [Some::class, Other::class]) + val x: Int = 42 + } +} + +class Some +class Other diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java index 37d19f33d56..57e0f8c6786 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java @@ -6747,6 +6747,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/falseSamConversion.kt"); } + @Test + @TestMetadata("FieldAnnotationWithClasses.kt") + public void testFieldAnnotationWithClasses() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt"); + } + @Test @TestMetadata("immutableName.kt") public void testImmutableName() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java index 6cb27480e43..b577393b34b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java @@ -6747,6 +6747,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/falseSamConversion.kt"); } + @Test + @TestMetadata("FieldAnnotationWithClasses.kt") + public void testFieldAnnotationWithClasses() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/FieldAnnotationWithClasses.kt"); + } + @Test @TestMetadata("immutableName.kt") public void testImmutableName() throws Exception { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmFieldApplicabilityChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmFieldApplicabilityChecker.kt index 4d20eff7530..a5c19bba0c1 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmFieldApplicabilityChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmFieldApplicabilityChecker.kt @@ -39,7 +39,8 @@ import org.jetbrains.kotlin.name.StandardClassIds object FirJvmFieldApplicabilityChecker : FirPropertyChecker() { override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { val session = context.session - val annotation = declaration.getAnnotationByClassId(JVM_FIELD_ANNOTATION_CLASS_ID, session) ?: return + val annotation = declaration.backingField?.getAnnotationByClassId(JVM_FIELD_ANNOTATION_CLASS_ID, session) + ?: return val containingClassSymbol = declaration.containingClassLookupTag()?.toFirRegularClassSymbol(session) val problem = when { @@ -125,7 +126,7 @@ object FirJvmFieldApplicabilityChecker : FirPropertyChecker() { } private fun FirPropertySymbol.hasJvmFieldAnnotation(session: FirSession): Boolean { - return getAnnotationByClassId(JVM_FIELD_ANNOTATION_CLASS_ID, session) != null + return backingFieldSymbol?.getAnnotationByClassId(JVM_FIELD_ANNOTATION_CLASS_ID, session) != null } private fun isInsideJvmMultifileClassFile(context: CheckerContext): Boolean { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt index 4235d8e7e2e..9694d23a641 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmStaticChecker.kt @@ -199,10 +199,8 @@ object FirJvmStaticChecker : FirBasicDeclarationChecker() { reporter: DiagnosticReporter, targetSource: KtSourceElement?, ) { - if ( - declaration is FirProperty && declaration.isConst || - declaration.hasAnnotationNamedAs(StandardClassIds.Annotations.JvmField) - ) { + if (declaration !is FirProperty) return + if (declaration.isConst || declaration.backingField?.hasAnnotationNamedAs(StandardClassIds.Annotations.JvmField) == true) { reporter.reportOn(targetSource, FirJvmErrors.JVM_STATIC_ON_CONST_OR_JVM_FIELD, context) } } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmSyntheticApplicabilityChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmSyntheticApplicabilityChecker.kt index 8771b6a6444..9d7a3ce83f4 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmSyntheticApplicabilityChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmSyntheticApplicabilityChecker.kt @@ -17,7 +17,7 @@ import org.jetbrains.kotlin.name.JvmNames.JVM_SYNTHETIC_ANNOTATION_CLASS_ID object FirJvmSyntheticApplicabilityChecker : FirPropertyChecker() { override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { - val annotation = declaration.delegateFieldSymbol?.getAnnotationByClassId(JVM_SYNTHETIC_ANNOTATION_CLASS_ID, context.session) + val annotation = declaration.backingField?.getAnnotationByClassId(JVM_SYNTHETIC_ANNOTATION_CLASS_ID, context.session) if (annotation != null && annotation.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) { reporter.reportOn(annotation.source, FirJvmErrors.JVM_SYNTHETIC_ON_DELEGATE, context) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 46b8a677f52..7e79d69f410 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -587,11 +587,15 @@ fun FirFunctionSymbol<*>.isFunctionForExpectTypeFromCastFeature(): Boolean { return true } -fun getActualTargetList(annotated: FirAnnotationContainer): AnnotationTargetList { +fun getActualTargetList(container: FirAnnotationContainer): AnnotationTargetList { fun CallableId.isMember(): Boolean { return classId != null || isLocal // TODO: Replace with .containingClass (after fixing) } + val annotated = + if (container is FirBackingField && !container.propertySymbol.hasBackingField) container.propertyIfBackingField + else container + return when (annotated) { is FirRegularClass -> { AnnotationTargetList( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationChecker.kt index b3cdb294732..ab520320446 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationChecker.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField +import org.jetbrains.kotlin.fir.delegatedWrapperData import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.packageFqName @@ -118,8 +119,8 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() { propertySymbol.resolvedReceiverTypeRef == null && propertySymbol.resolvedContextReceivers.isEmpty() val (hint, type) = when (annotation.useSiteTarget) { - FIELD -> "fields" to ((declaration as? FirProperty)?.backingField?.returnTypeRef ?: return) - PROPERTY_DELEGATE_FIELD -> "delegate fields" to ((declaration as? FirProperty)?.delegate?.typeRef ?: return) + FIELD -> "fields" to ((declaration as? FirBackingField)?.returnTypeRef ?: return) + PROPERTY_DELEGATE_FIELD -> "delegate fields" to ((declaration as? FirBackingField)?.propertySymbol?.delegate?.typeRef ?: return) RECEIVER -> "receivers" to ((declaration as? FirCallableDeclaration)?.receiverParameter?.typeRef ?: return) FILE, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, CONSTRUCTOR_PARAMETER, SETTER_PARAMETER, null -> when { declaration is FirProperty && !declaration.isLocal -> { @@ -216,12 +217,15 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() { ) } FIELD -> { - if (annotated is FirProperty && annotated.delegateFieldSymbol != null && !annotated.hasBackingField) { - reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD, context) + if (annotated is FirBackingField) { + val propertySymbol = annotated.propertySymbol + if (propertySymbol.delegateFieldSymbol != null && !propertySymbol.hasBackingField) { + reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD, context) + } } } PROPERTY_DELEGATE_FIELD -> { - if (annotated is FirProperty && annotated.delegateFieldSymbol == null) { + if (annotated is FirBackingField && annotated.propertySymbol.delegateFieldSymbol == null) { reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE, context) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirVolatileAnnotationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirVolatileAnnotationChecker.kt index c51108d8f1d..50bcad90b55 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirVolatileAnnotationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirVolatileAnnotationChecker.kt @@ -24,8 +24,7 @@ object FirVolatileAnnotationChecker : FirPropertyChecker() { override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration.source?.kind != KtRealSourceElementKind) return - val fieldAnnotation = declaration.annotations.getAnnotationByClassIds(VOLATILE_CLASS_IDS, context.session) - ?: declaration.backingField?.annotations?.getAnnotationByClassIds(VOLATILE_CLASS_IDS, context.session) + val fieldAnnotation = declaration.backingField?.annotations?.getAnnotationByClassIds(VOLATILE_CLASS_IDS, context.session) ?: return if (!declaration.isVar) { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt index 2869484ffe3..338c44079e1 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/AnnotationGenerator.kt @@ -5,28 +5,25 @@ package org.jetbrains.kotlin.fir.backend.generators -import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.backend.Fir2IrComponents +import org.jetbrains.kotlin.fir.declarations.FirBackingField import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirValueParameter -import org.jetbrains.kotlin.fir.declarations.useSiteTargetsFromMetaAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.util.isSetter /** * A generator that converts annotations in [FirAnnotationContainer] to annotations in [IrMutableAnnotationContainer]. * - * In general, annotations are bound to the target in the beginning, i.e., clearly targeted at source level. But, there are some cases that - * need special handling: [AnnotationUseSiteTarget]. In particular, [FirProperty] contains all annotations associated with that property, - * whose targets may vary. After all the necessary pieces of IR elements, e.g., backing field, are ready, this generator splits those - * annotations to the specified targets. + * Annotations are bound to the target already in frontend, e.g. * - * Note: Annotations on primary constructor properties are already split between value parameters and properties in FIR. Before this change, - * it used to be done here. + * + Annotations on primary constructor properties are already split between value parameters, properties and backing fields in FIR. + * + Annotations on regular properties are also already split between properties and backing fields. + * + * So this class task is only to convert FirAnnotations to IrAnnotations. + * Some time before, it performed also annotation splitting between use-site targets. */ class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComponents by components { @@ -39,56 +36,15 @@ class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComp irContainer.annotations = firContainer.annotations.toIrAnnotations() } - private fun FirAnnotation.target(applicable: List): AnnotationUseSiteTarget? = - useSiteTarget ?: applicable.firstOrNull(useSiteTargetsFromMetaAnnotation(session)::contains) - - companion object { - private val propertyTargets = listOf(AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.FIELD) - private val delegatedPropertyTargets = propertyTargets + listOf(AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD) - } - fun generate(irValueParameter: IrValueParameter, firValueParameter: FirValueParameter) { irValueParameter.annotations += firValueParameter.annotations.toIrAnnotations() } fun generate(irProperty: IrProperty, property: FirProperty) { - val applicableTargets = if (irProperty.isDelegated) delegatedPropertyTargets else propertyTargets - irProperty.annotations += property.annotations - .filter { it.target(applicableTargets) == AnnotationUseSiteTarget.PROPERTY } - .toIrAnnotations() + irProperty.annotations += property.annotations.toIrAnnotations() } - fun generate(irField: IrField, property: FirProperty) { - val irProperty = irField.correspondingPropertySymbol?.owner ?: throw AssertionError("$irField is not a property field") - - val applicableTargets = if (irProperty.isDelegated) delegatedPropertyTargets else propertyTargets - property.backingField?.let { - irField.annotations += it.annotations.filter { - val target = it.target(applicableTargets) - target == AnnotationUseSiteTarget.FIELD || target == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD - }.toIrAnnotations() - } - } - - fun generate(propertyAccessor: IrFunction, property: FirProperty) { - assert(propertyAccessor.isPropertyAccessor) { "$propertyAccessor is not a property accessor." } - if (propertyAccessor.isSetter) { - propertyAccessor.annotations += property.annotations - .filter { it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_SETTER } - .toIrAnnotations() - val parameter = propertyAccessor.valueParameters.last() - parameter.annotations += property.annotations - .filter { it.useSiteTarget == AnnotationUseSiteTarget.SETTER_PARAMETER } - .toIrAnnotations() - } else { - propertyAccessor.annotations += property.annotations - .filter { it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_GETTER } - .toIrAnnotations() - } - propertyAccessor.extensionReceiverParameter?.let { receiver -> - receiver.annotations += property.annotations - .filter { it.useSiteTarget == AnnotationUseSiteTarget.RECEIVER } - .toIrAnnotations() - } + fun generate(irField: IrField, backingField: FirBackingField) { + irField.annotations += backingField.annotations.toIrAnnotations() } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt index c7dc007628f..fa6102bf170 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/ClassMemberGenerator.kt @@ -267,7 +267,7 @@ internal class ClassMemberGenerator( } declarationStorage.leaveScope(this@initializeBackingField) } - annotationGenerator.generate(irField, property) + property.backingField?.let { annotationGenerator.generate(irField, it) } } private fun IrSimpleFunction.setPropertyAccessorContent( @@ -314,7 +314,6 @@ internal class ClassMemberGenerator( } } - annotationGenerator.generate(this, property) } private fun IrFieldAccessExpression.setReceiver(declaration: IrDeclaration): IrFieldAccessExpression { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt index 762eb03e8b9..370848ed483 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DelegatedMemberGenerator.kt @@ -318,13 +318,6 @@ class DelegatedMemberGenerator(private val components: Fir2IrComponents) : Fir2I basePropertySymbols[delegateProperty] = baseSymbols annotationGenerator.generate(delegateProperty, firDelegateProperty) - val getter = delegateProperty.getter!! - annotationGenerator.generate(getter, firDelegateProperty) - if (delegateProperty.isVar) { - val setter = delegateProperty.setter!! - annotationGenerator.generate(setter, firDelegateProperty) - } - return delegateProperty } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt index d9b3f7f0548..b81cd1c0d7a 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt @@ -154,6 +154,9 @@ class Fir2IrLazyProperty( } }?.apply { this.parent = this@Fir2IrLazyProperty.parent + this.annotations = fir.backingField?.annotations?.mapNotNull { + callGenerator.convertToIrConstructorCall(it) as? IrConstructorCall + }.orEmpty() } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 0bce4b75e82..294a056a146 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -70,7 +70,8 @@ fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List, ): FirBackingField { var modifiers = Modifier() var returnType: FirTypeRef = implicitType @@ -1532,6 +1538,7 @@ class DeclarationsConverter( symbol = FirBackingFieldSymbol(CallableId(name)) this.status = status annotations += modifiers.annotations + annotations += annotationsFromProperty this.propertySymbol = propertySymbol this.initializer = backingFieldInitializer this.isVar = isVar @@ -1540,7 +1547,7 @@ class DeclarationsConverter( } else { FirDefaultPropertyBackingField( moduleData = baseModuleData, - annotations = mutableListOf(), + annotations = annotationsFromProperty.toMutableList(), returnTypeRef = propertyReturnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor), isVar = isVar, propertySymbol = propertySymbol, diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/ValueParameter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/ValueParameter.kt index 59306118f6d..4bd1a8cb210 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/ValueParameter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/ValueParameter.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.lightTree.fir import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.* import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.builder.Context @@ -136,16 +137,16 @@ class ValueParameter( } backingField = FirDefaultPropertyBackingField( moduleData = moduleData, - annotations = mutableListOf(), + annotations = modifiers.annotations.filter { + it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD + }.toMutableList(), returnTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor), isVar = isVar, propertySymbol = symbol, status = status.copy(), ) annotations += modifiers.annotations.filter { - it.useSiteTarget == null || it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY || - it.useSiteTarget == AnnotationUseSiteTarget.FIELD || - it.useSiteTarget == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD + it.useSiteTarget == null || it.useSiteTarget == PROPERTY } val defaultAccessorSource = propertySource?.fakeElement(KtFakeSourceElementKind.DefaultAccessor) getter = FirDefaultPropertyGetter( diff --git a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java index a53b6d2c235..54c4607df5b 100644 --- a/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java +++ b/compiler/fir/raw-fir/light-tree2fir/tests-gen/org/jetbrains/kotlin/fir/lightTree/LightTree2FirConverterTestCaseGenerated.java @@ -46,6 +46,16 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationsOnNullableParenthesizedTypes.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index a70103e2f11..4803a25dd5f 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -540,6 +540,7 @@ open class RawFirBuilder( property: KtProperty, propertySymbol: FirPropertySymbol, propertyReturnType: FirTypeRef, + annotationsFromProperty: List, ): FirBackingField { val componentVisibility = if (this?.visibility != null && this.visibility != Visibilities.Unknown) { this.visibility @@ -558,6 +559,7 @@ open class RawFirBuilder( returnTypeRef = returnType this.status = status extractAnnotationsTo(this) + this.annotations += annotationsFromProperty name = BACKING_FIELD symbol = FirBackingFieldSymbol(CallableId(name)) this.propertySymbol = propertySymbol @@ -570,7 +572,7 @@ open class RawFirBuilder( } else { FirDefaultPropertyBackingField( moduleData = baseModuleData, - annotations = mutableListOf(), + annotations = annotationsFromProperty.toMutableList(), returnTypeRef = propertyReturnType.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor), isVar = property.isVar, propertySymbol = propertySymbol, @@ -654,7 +656,9 @@ open class RawFirBuilder( isLocal = false backingField = FirDefaultPropertyBackingField( moduleData = baseModuleData, - annotations = mutableListOf(), + annotations = parameterAnnotations.filter { + it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD + }.toMutableList(), returnTypeRef = returnTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.DefaultAccessor), isVar = isVar, propertySymbol = symbol, @@ -687,9 +691,7 @@ open class RawFirBuilder( setter.replaceAnnotations(parameterAnnotations.filterUseSiteTarget(PROPERTY_SETTER)) } else null annotations += parameterAnnotations.filter { - it.useSiteTarget == null || it.useSiteTarget == PROPERTY || - it.useSiteTarget == FIELD || - it.useSiteTarget == PROPERTY_DELEGATE_FIELD + it.useSiteTarget == null || it.useSiteTarget == PROPERTY } dispatchReceiverType = currentDispatchReceiverType() @@ -1767,6 +1769,7 @@ open class RawFirBuilder( this@toFirProperty, propertySymbol = symbol, propertyType, + emptyList(), ) status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL).apply { @@ -1801,6 +1804,7 @@ open class RawFirBuilder( this@toFirProperty, propertySymbol = symbol, propertyType, + propertyAnnotations.filter { it.useSiteTarget == FIELD || it.useSiteTarget == PROPERTY_DELEGATE_FIELD }, ) getter = this@toFirProperty.getter.toFirPropertyAccessor( @@ -1856,7 +1860,7 @@ open class RawFirBuilder( } } annotations += if (isLocal) propertyAnnotations else propertyAnnotations.filter { - it.useSiteTarget != PROPERTY_GETTER && + it.useSiteTarget != FIELD && it.useSiteTarget != PROPERTY_DELEGATE_FIELD && it.useSiteTarget != PROPERTY_GETTER && (!isVar || it.useSiteTarget != SETTER_PARAMETER && it.useSiteTarget != PROPERTY_SETTER) } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt new file mode 100644 index 00000000000..006381feb39 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt @@ -0,0 +1,36 @@ +import kotlin.reflect.KProperty + +annotation class Ann + +class CustomDelegate { + operator fun getValue(thisRef: Any?, prop: KProperty<*>): String = prop.name +} + +@field:Ann +class SomeClass { + + @field:Ann + constructor() + + @field:Ann + protected val simpleProperty: String = "text" + + @field:[Ann] + protected val simplePropertyWithAnnotationList: String = "text" + + @field:Ann + protected val delegatedProperty: String by CustomDelegate() + + @field:Ann + val propertyWithCustomGetter: Int + get() = 5 + + @field:Ann + fun anotherFun(@field:Ann s: String) { + @field:Ann + val localVariable = 5 + } + +} + +class WithPrimaryConstructor(@field:Ann val a: String) diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.lazyBodies.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.lazyBodies.txt new file mode 100644 index 00000000000..46f370e5f42 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.lazyBodies.txt @@ -0,0 +1,46 @@ +FILE: annotationOnField.kt + public? final? annotation class Ann : R|kotlin/Annotation| { + public? constructor(): R|Ann| { + LAZY_super + } + + } + public? final? class CustomDelegate : R|kotlin/Any| { + public? constructor(): R|CustomDelegate| { + LAZY_super + } + + public? final? operator fun getValue(thisRef: Any?, prop: KProperty<*>): String { LAZY_BLOCK } + + } + @FIELD:Ann() public? final? class SomeClass : R|kotlin/Any| { + @FIELD:Ann() public? constructor(): R|SomeClass| { + LAZY_super + } + + field:@FIELD:Ann() protected final? val simpleProperty: String = LAZY_EXPRESSION + protected get(): String + + field:@FIELD:Ann() protected final? val simplePropertyWithAnnotationList: String = LAZY_EXPRESSION + protected get(): String + + field:@FIELD:Ann() protected final? val delegatedProperty: Stringby LAZY_EXPRESSION + protected get(): { + ^ this@R|/SomeClass|.D|/SomeClass.delegatedProperty|.getValue#(this@R|/SomeClass|, ::R|/SomeClass.delegatedProperty|) + } + + field:@FIELD:Ann() public? final? val propertyWithCustomGetter: Int + public? get(): Int { LAZY_BLOCK } + + @FIELD:Ann() public? final? fun anotherFun(@FIELD:Ann() s: String): R|kotlin/Unit| { LAZY_BLOCK } + + } + public? final? class WithPrimaryConstructor : R|kotlin/Any| { + public? constructor(a: String): R|WithPrimaryConstructor| { + LAZY_super + } + + field:@FIELD:Ann() public? final? val a: String = R|/a| + public? get(): String + + } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.txt new file mode 100644 index 00000000000..1308b292397 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.txt @@ -0,0 +1,52 @@ +FILE: annotationOnField.kt + public? final? annotation class Ann : R|kotlin/Annotation| { + public? [ContainingClassKey=Ann] constructor(): R|Ann| { + super() + } + + } + public? final? class CustomDelegate : R|kotlin/Any| { + public? [ContainingClassKey=CustomDelegate] constructor(): R|CustomDelegate| { + super() + } + + public? final? operator fun getValue(thisRef: Any?, prop: KProperty<*>): String { + ^getValue prop#.name# + } + + } + @FIELD:Ann() public? final? class SomeClass : R|kotlin/Any| { + @FIELD:Ann() public? [ContainingClassKey=SomeClass] constructor(): R|SomeClass| { + super() + } + + field:@FIELD:Ann() protected final? val simpleProperty: String = String(text) + protected [ContainingClassKey=SomeClass] get(): String + + field:@FIELD:Ann() protected final? val simplePropertyWithAnnotationList: String = String(text) + protected [ContainingClassKey=SomeClass] get(): String + + field:@FIELD:Ann() protected final? val delegatedProperty: Stringby CustomDelegate#() + protected [ContainingClassKey=SomeClass] get(): { + ^ this@R|/SomeClass|.D|/SomeClass.delegatedProperty|.getValue#(this@R|/SomeClass|, ::R|/SomeClass.delegatedProperty|) + } + + field:@FIELD:Ann() public? final? val propertyWithCustomGetter: Int + public? [ContainingClassKey=SomeClass] get(): Int { + ^ IntegerLiteral(5) + } + + @FIELD:Ann() public? final? fun anotherFun(@FIELD:Ann() s: String): R|kotlin/Unit| { + @FIELD:Ann() lval localVariable: = IntegerLiteral(5) + } + + } + public? final? class WithPrimaryConstructor : R|kotlin/Any| { + public? [ContainingClassKey=WithPrimaryConstructor] constructor([CorrespondingProperty=/WithPrimaryConstructor.a] a: String): R|WithPrimaryConstructor| { + super() + } + + field:@FIELD:Ann() public? final? [IsFromPrimaryConstructor=true] val a: String = R|/a| + public? [ContainingClassKey=WithPrimaryConstructor] get(): String + + } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt new file mode 100644 index 00000000000..f9019063e8b --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt @@ -0,0 +1,21 @@ +annotation class Ann + +@field:Ann +val x: Int = 1 + +@property:Ann +val y: Int = 2 + +@Ann +val z: Int = 3 + +class Some(@field:Ann val x: Int, @property: Ann val y: Int, @param:Ann val z: Int, val w: Int) { + @field:Ann + val a: Int = 1 + + @property:Ann + val b: Int = 2 + + @Ann + val c: Int = 3 +} diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.lazyBodies.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.lazyBodies.txt new file mode 100644 index 00000000000..7938e377bbf --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.lazyBodies.txt @@ -0,0 +1,40 @@ +FILE: annotationOnProperty.kt + public? final? annotation class Ann : R|kotlin/Annotation| { + public? constructor(): R|Ann| { + LAZY_super + } + + } + field:@FIELD:Ann() public? final? val x: Int = LAZY_EXPRESSION + public? get(): Int + @PROPERTY:Ann() public? final? val y: Int = LAZY_EXPRESSION + public? get(): Int + @Ann() public? final? val z: Int = LAZY_EXPRESSION + public? get(): Int + public? final? class Some : R|kotlin/Any| { + public? constructor(x: Int, y: Int, @CONSTRUCTOR_PARAMETER:Ann() z: Int, w: Int): R|Some| { + LAZY_super + } + + field:@FIELD:Ann() public? final? val x: Int = R|/x| + public? get(): Int + + @PROPERTY:Ann() public? final? val y: Int = R|/y| + public? get(): Int + + public? final? val z: Int = R|/z| + public? get(): Int + + public? final? val w: Int = R|/w| + public? get(): Int + + field:@FIELD:Ann() public? final? val a: Int = LAZY_EXPRESSION + public? get(): Int + + @PROPERTY:Ann() public? final? val b: Int = LAZY_EXPRESSION + public? get(): Int + + @Ann() public? final? val c: Int = LAZY_EXPRESSION + public? get(): Int + + } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.txt new file mode 100644 index 00000000000..9ec3750ef03 --- /dev/null +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.txt @@ -0,0 +1,40 @@ +FILE: annotationOnProperty.kt + public? final? annotation class Ann : R|kotlin/Annotation| { + public? [ContainingClassKey=Ann] constructor(): R|Ann| { + super() + } + + } + field:@FIELD:Ann() public? final? val x: Int = IntegerLiteral(1) + public? get(): Int + @PROPERTY:Ann() public? final? val y: Int = IntegerLiteral(2) + public? get(): Int + @Ann() public? final? val z: Int = IntegerLiteral(3) + public? get(): Int + public? final? class Some : R|kotlin/Any| { + public? [ContainingClassKey=Some] constructor([CorrespondingProperty=/Some.x] x: Int, [CorrespondingProperty=/Some.y] y: Int, [CorrespondingProperty=/Some.z] @CONSTRUCTOR_PARAMETER:Ann() z: Int, [CorrespondingProperty=/Some.w] w: Int): R|Some| { + super() + } + + field:@FIELD:Ann() public? final? [IsFromPrimaryConstructor=true] val x: Int = R|/x| + public? [ContainingClassKey=Some] get(): Int + + @PROPERTY:Ann() public? final? [IsFromPrimaryConstructor=true] val y: Int = R|/y| + public? [ContainingClassKey=Some] get(): Int + + public? final? [IsFromPrimaryConstructor=true] val z: Int = R|/z| + public? [ContainingClassKey=Some] get(): Int + + public? final? [IsFromPrimaryConstructor=true] val w: Int = R|/w| + public? [ContainingClassKey=Some] get(): Int + + field:@FIELD:Ann() public? final? val a: Int = IntegerLiteral(1) + public? [ContainingClassKey=Some] get(): Int + + @PROPERTY:Ann() public? final? val b: Int = IntegerLiteral(2) + public? [ContainingClassKey=Some] get(): Int + + @Ann() public? final? val c: Int = IntegerLiteral(3) + public? [ContainingClassKey=Some] get(): Int + + } diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java index 82dac171269..efc92964582 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderLazyBodiesTestCaseGenerated.java @@ -46,6 +46,16 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationsOnNullableParenthesizedTypes.kt"); diff --git a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java index 13558ecb5c6..ef11f5528ca 100644 --- a/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java +++ b/compiler/fir/raw-fir/psi2fir/tests-gen/org/jetbrains/kotlin/fir/builder/RawFirBuilderTestCaseGenerated.java @@ -46,6 +46,16 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationsOnNullableParenthesizedTypes.kt"); diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt index b659a516591..4553b7582fb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTypeResolveTransformer.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers import kotlinx.collections.immutable.* import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.* import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.correspondingProperty @@ -147,7 +148,7 @@ open class FirTypeResolveTransformer( if (result.isPrimary) { for (valueParameter in result.valueParameters) { if (valueParameter.correspondingProperty != null) { - valueParameter.removeIrrelevantAnnotations() + valueParameter.moveOrDeleteIrrelevantAnnotations() } } } @@ -218,10 +219,7 @@ open class FirTypeResolveTransformer( unboundCyclesInTypeParametersSupertypes(property) - if (property.source?.kind == KtFakeSourceElementKind.PropertyFromParameter) { - property.removeIrrelevantAnnotations() - } - + property.moveOrDeleteIrrelevantAnnotations() calculateDeprecations(property) property } @@ -486,37 +484,43 @@ open class FirTypeResolveTransformer( * class Foo(@Ann val x: String) * ``` * This ambiguity may be resolved by specifying the use-site explicitly, i.e. `@field:Ann` or by analysing the allowed targets from - * the [kotlin.annotation.Target] meta-annotation. In latter case, the method will assign a use-site target to the corresponding - * annotation. + * the [kotlin.annotation.Target] meta-annotation. + * In latter case, the method will ensure that the annotation is moved to the correct element (field or parameter) or left at the property. */ - private fun FirVariable.removeIrrelevantAnnotations() { + private fun FirVariable.moveOrDeleteIrrelevantAnnotations() { + if (annotations.isEmpty()) return + val backingFieldAnnotations by lazy(LazyThreadSafetyMode.NONE) { backingField?.annotations?.toMutableList() ?: mutableListOf() } + var replaceBackingFieldAnnotations = false replaceAnnotations(annotations.filter { annotation -> - when(annotation.useSiteTarget) { + when (annotation.useSiteTarget) { null -> { val allowedTargets = annotation.useSiteTargetsFromMetaAnnotation(session) when { - this is FirValueParameter -> AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER in allowedTargets - this.source?.kind == KtFakeSourceElementKind.PropertyFromParameter && AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER in allowedTargets -> false - this is FirProperty && AnnotationUseSiteTarget.FIELD in allowedTargets && AnnotationUseSiteTarget.PROPERTY !in allowedTargets && backingField != null -> { - (this as? FirProperty)?.backingField?.replaceAnnotations(backingField!!.annotations + annotation) + this is FirValueParameter -> CONSTRUCTOR_PARAMETER in allowedTargets + this.source?.kind == KtFakeSourceElementKind.PropertyFromParameter && CONSTRUCTOR_PARAMETER in allowedTargets -> false + this is FirProperty && backingField != null && annotationShouldBeMovedToField(allowedTargets) -> { + backingFieldAnnotations += annotation + replaceBackingFieldAnnotations = true false } else -> true } } - AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> this is FirValueParameter - AnnotationUseSiteTarget.FIELD -> { - (this as? FirProperty)?.backingField?.replaceAnnotations(backingField!!.annotations + annotation) - false - } else -> true } }) + if (replaceBackingFieldAnnotations) { + backingField?.replaceAnnotations(backingFieldAnnotations) + } } + private fun annotationShouldBeMovedToField(allowedTargets: Set): Boolean = + (FIELD in allowedTargets || PROPERTY_DELEGATE_FIELD in allowedTargets) && PROPERTY !in allowedTargets + private fun calculateDeprecations(callableDeclaration: FirCallableDeclaration) { if (callableDeclaration.deprecationsProvider is UnresolvedDeprecationProvider) { callableDeclaration.replaceDeprecationsProvider(callableDeclaration.getDeprecationsProvider(session)) } } + } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index bfd28a9818f..8aef3fdeb69 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -128,7 +128,7 @@ open class FirDeclarationsResolveTransformer( val bodyResolveState = property.bodyResolveState if (bodyResolveState == FirPropertyBodyResolveState.EVERYTHING_RESOLVED) return property - val canHaveDeepImplicitTypeRefs = property.hasExplicitBackingField + val canHaveDeepImplicitTypeRefs = property.backingField != null if (returnTypeRefBeforeResolve !is FirImplicitTypeRef && implicitTypeOnly && !canHaveDeepImplicitTypeRefs) { return property } @@ -930,6 +930,7 @@ open class FirDeclarationsResolveTransformer( else -> ResolutionMode.ContextDependent } backingField.transformInitializer(transformer, initializerData) + backingField.transformAnnotations(transformer, data) if ( backingField.returnTypeRef is FirErrorTypeRef || backingField.returnTypeRef is FirResolvedTypeRef diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirImplicitBodyResolve.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirImplicitBodyResolve.kt index 677ae207345..72419db6741 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirImplicitBodyResolve.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirImplicitBodyResolve.kt @@ -167,7 +167,7 @@ open class FirImplicitAwareBodyResolveTransformer( return transform() } - val canHaveDeepImplicitTypeRefs = member is FirProperty && member.hasExplicitBackingField + val canHaveDeepImplicitTypeRefs = member is FirProperty && member.backingField != null if (member.returnTypeRef is FirResolvedTypeRef && !canHaveDeepImplicitTypeRefs) return member val symbol = member.symbol diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirAnnotationArgumentsMappingTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirAnnotationArgumentsMappingTransformer.kt index 2405ff9b178..bd8763a441f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirAnnotationArgumentsMappingTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirAnnotationArgumentsMappingTransformer.kt @@ -171,6 +171,7 @@ private class FirDeclarationsResolveTransformerForAnnotationArgumentsMapping( .transformReturnTypeRef(transformer, data) .transformGetter(transformer, data) .transformSetter(transformer, data) + .transformBackingField(transformer, data) } return property @@ -212,6 +213,11 @@ private class FirDeclarationsResolveTransformerForAnnotationArgumentsMapping( return field } + override fun transformBackingField(backingField: FirBackingField, data: ResolutionMode): FirStatement { + backingField.transformAnnotations(transformer, data) + return backingField + } + override fun transformTypeAlias(typeAlias: FirTypeAlias, data: ResolutionMode): FirTypeAlias { doTransformTypeParameters(typeAlias) typeAlias.transformAnnotations(transformer, data) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirRenderer.kt index 87d3e9ce2e5..e8608e7c7a4 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirRenderer.kt @@ -199,6 +199,13 @@ class FirRenderer( override fun visitCallableDeclaration(callableDeclaration: FirCallableDeclaration) { renderContexts(callableDeclaration.contextReceivers) annotationRenderer?.render(callableDeclaration) + if (callableDeclaration is FirProperty) { + val backingField = callableDeclaration.backingField + if (backingField?.annotations?.isNotEmpty() == true) { + print("field:") + annotationRenderer?.render(backingField) + } + } visitMemberDeclaration(callableDeclaration) val receiverParameter = callableDeclaration.receiverParameter if (callableDeclaration !is FirProperty || callableDeclaration.isCatchParameter != true) { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt index f44472102c3..eabf36674f2 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt @@ -38,6 +38,12 @@ open class FirPropertySymbol( val delegateFieldSymbol: FirDelegateFieldSymbol? get() = fir.delegateFieldSymbol + val delegate: FirExpression? + get() = fir.delegate + + val hasDelegate: Boolean + get() = fir.delegate != null + val hasInitializer: Boolean get() = fir.initializer != null @@ -47,9 +53,6 @@ open class FirPropertySymbol( return fir.initializer } - val hasDelegate: Boolean - get() = fir.delegate != null - val controlFlowGraphReference: FirControlFlowGraphReference? get() { lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) diff --git a/compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.fir.txt b/compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.fir.txt index 50739e0ba22..03e4600e198 100644 --- a/compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.fir.txt +++ b/compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.fir.txt @@ -4,7 +4,7 @@ FILE: test.kt super() } - @R|FieldOnly|() public final var param: R|kotlin/Int| = R|/param| + field:@R|FieldOnly|() public final var param: R|kotlin/Int| = R|/param| public get(): R|kotlin/Int| public set(value: R|kotlin/Int|): R|kotlin/Unit| diff --git a/compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.fir.txt b/compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.fir.txt index 0eec06f2927..bb6c12f1ed5 100644 --- a/compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.fir.txt +++ b/compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.fir.txt @@ -40,7 +40,7 @@ FILE: targetOnPrimaryCtorParameter.kt super() } - @R|PropertyOnly|() @R|PropertyOnly2|() @R|FieldOnly|() public final var param: R|kotlin/Int| = R|/param| + @R|PropertyOnly|() @R|PropertyOnly2|() field:@R|FieldOnly|() public final var param: R|kotlin/Int| = R|/param| public get(): R|kotlin/Int| public set(value: R|kotlin/Int|): R|kotlin/Unit| diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java index 19368fd15ce..b07ece675c7 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/fir/FirVisualizerForRawFirDataGenerated.java @@ -39,6 +39,18 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @Test + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @Test + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @Test @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { diff --git a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java index 9053e1e5750..bb0a50409d2 100644 --- a/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java +++ b/compiler/visualizer/tests-gen/org/jetbrains/kotlin/visualizer/psi/PsiVisualizerForRawFirDataGenerated.java @@ -39,6 +39,18 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotation.kt"); } + @Test + @TestMetadata("annotationOnField.kt") + public void testAnnotationOnField() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnField.kt"); + } + + @Test + @TestMetadata("annotationOnProperty.kt") + public void testAnnotationOnProperty() throws Exception { + runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/declarations/annotationOnProperty.kt"); + } + @Test @TestMetadata("annotationsOnNullableParenthesizedTypes.kt") public void testAnnotationsOnNullableParenthesizedTypes() throws Exception { diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt index 80a38e436bd..60623a13d23 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -231,7 +231,8 @@ object FirSerializationPluginClassChecker : FirClassChecker() { if (classSymbol.resolvedSuperTypes.any { it.classId == JAVA_SERIALIZABLE_ID }) return // do not check for (property in properties) { if (property.transient) continue - val incorrectTransient = property.propertySymbol.annotations.getAnnotationByClassId(TRANSIENT_ANNOTATION_CLASS_ID, session) + val incorrectTransient = + property.propertySymbol.backingFieldSymbol?.annotations?.getAnnotationByClassId(TRANSIENT_ANNOTATION_CLASS_ID, session) if (incorrectTransient != null) { reporter.reportOn( source = incorrectTransient.source ?: property.propertySymbol.source,