diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.kt new file mode 100644 index 00000000000..1171701f399 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.kt @@ -0,0 +1,31 @@ +// LOOK_UP_FOR_ELEMENT_OF_TYPE: org.jetbrains.kotlin.psi.KtProperty +// FILE: KotlinFile.kt +package one.three + +import one.four.Manager + +class KotlinFile { + private val branchManager: Manager? = null +} + +// FILE: one/four/Manager.java +package one.four; + +import one.two.Service; +import static one.three.KotlinFile.*; + +@Service(Service.Level.PROJECT) +public class Manager { +} + +// FILE: one/two/Service.java +package one.two; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public @interface Service { + Level[] value() default Level.APP; + + enum Level {APP, PROJECT} +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.out_of_src_roots.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.out_of_src_roots.txt new file mode 100644 index 00000000000..f16f85153ad --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.out_of_src_roots.txt @@ -0,0 +1,22 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +private final [ResolvedTo(BODY_RESOLVE)] val branchManager: = Null(null) + private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] KotlinFile.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + package one.three + + public final [ResolvedTo(STATUS)] class KotlinFile : R|kotlin/Any| { + public [ResolvedTo(STATUS)] [ContainingClassKey=KotlinFile] constructor(): R|one/three/KotlinFile| { + LAZY_super + } + + private final [ResolvedTo(BODY_RESOLVE)] val branchManager: = Null(null) + private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): + + } diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.txt new file mode 100644 index 00000000000..011a909b682 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.txt @@ -0,0 +1,22 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +private final [ResolvedTo(BODY_RESOLVE)] val branchManager: R|one/four/Manager?| = Null(null) + private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): R|one/four/Manager?| + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] KotlinFile.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + package one.three + + public final [ResolvedTo(STATUS)] class KotlinFile : R|kotlin/Any| { + public [ResolvedTo(STATUS)] [ContainingClassKey=KotlinFile] constructor(): R|one/three/KotlinFile| { + LAZY_super + } + + private final [ResolvedTo(BODY_RESOLVE)] val branchManager: R|one/four/Manager?| = Null(null) + private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): R|one/four/Manager?| + + } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java index 52285f5405c..7e256fba4e4 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java @@ -507,6 +507,12 @@ public class OutOfContentRootGetOrBuildFirTestGenerated extends AbstractOutOfCon runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/destructuringEntry.kt"); } + @Test + @TestMetadata("kt60387.kt") + public void testKt60387() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.kt"); + } + @Test @TestMetadata("ktij23263.kt") public void testKtij23263() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java index 774eded9f2f..d341d16c269 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java @@ -507,6 +507,12 @@ public class SourceGetOrBuildFirTestGenerated extends AbstractSourceGetOrBuildFi runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/destructuringEntry.kt"); } + @Test + @TestMetadata("kt60387.kt") + public void testKt60387() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/declarations/kt60387.kt"); + } + @Test @TestMetadata("ktij23263.kt") public void testKtij23263() throws Exception { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/javaAnnotationsMapping.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/javaAnnotationsMapping.kt index a0aab91cdc8..f5041aa4012 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/javaAnnotationsMapping.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/javaAnnotationsMapping.kt @@ -5,7 +5,9 @@ package org.jetbrains.kotlin.fir.java +import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.FirAnnotationContainer +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirConstructor @@ -14,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirAnnotationArgumentMapping import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression import org.jetbrains.kotlin.fir.expressions.buildUnaryArgumentList @@ -35,6 +38,8 @@ import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef +import org.jetbrains.kotlin.fir.visitors.FirTransformer +import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.load.java.structure.impl.JavaElementImpl import org.jetbrains.kotlin.name.ClassId @@ -43,6 +48,7 @@ import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.toKtPsiSourceElement import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import java.util.* internal fun Iterable.convertAnnotationsToFir( @@ -210,10 +216,9 @@ private fun JavaAnnotationArgument.mapJavaRetentionArgument(session: FirSession) private fun fillAnnotationArgumentMapping( session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, lookupTag: ConeClassLikeLookupTagImpl, annotationArguments: Collection, - destination: MutableMap + destination: MutableMap, ) { if (annotationArguments.isEmpty()) return @@ -226,56 +231,71 @@ private fun fillAnnotationArgumentMapping( annotationArguments.associateTo(destination) { argument -> val name = argument.name ?: StandardClassIds.Annotations.ParameterNames.value val parameter = annotationConstructor?.valueParameters?.find { it.name == name } - name to argument.toFirExpression(session, javaTypeParameterStack, parameter?.returnTypeRef) + name to argument.toFirExpression(session, JavaTypeParameterStack.EMPTY, parameter?.returnTypeRef) } } private fun JavaAnnotation.toFirAnnotationCall( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack -): FirAnnotation { - return buildAnnotation { - val lookupTag = when (classId) { - StandardClassIds.Annotations.Java.Target -> StandardClassIds.Annotations.Target - StandardClassIds.Annotations.Java.Retention -> StandardClassIds.Annotations.Retention - StandardClassIds.Annotations.Java.Documented -> StandardClassIds.Annotations.MustBeDocumented - StandardClassIds.Annotations.Java.Deprecated -> StandardClassIds.Annotations.Deprecated - else -> classId - }?.toLookupTag() - annotationTypeRef = if (lookupTag != null) { - buildResolvedTypeRef { - type = ConeClassLikeTypeImpl(lookupTag, emptyArray(), isNullable = false) - } - } else { - val unresolvedName = classId?.shortClassName ?: SpecialNames.NO_NAME_PROVIDED - buildErrorTypeRef { diagnostic = ConeUnresolvedReferenceError(unresolvedName) } +): FirAnnotation = buildAnnotation { + val lookupTag = when (classId) { + StandardClassIds.Annotations.Java.Target -> StandardClassIds.Annotations.Target + StandardClassIds.Annotations.Java.Retention -> StandardClassIds.Annotations.Retention + StandardClassIds.Annotations.Java.Documented -> StandardClassIds.Annotations.MustBeDocumented + StandardClassIds.Annotations.Java.Deprecated -> StandardClassIds.Annotations.Deprecated + else -> classId + }?.toLookupTag() + annotationTypeRef = if (lookupTag != null) { + buildResolvedTypeRef { + type = ConeClassLikeTypeImpl(lookupTag, emptyArray(), isNullable = false) } + } else { + val unresolvedName = classId?.shortClassName ?: SpecialNames.NO_NAME_PROVIDED + buildErrorTypeRef { diagnostic = ConeUnresolvedReferenceError(unresolvedName) } + } - argumentMapping = buildAnnotationArgumentMapping { - when (classId) { - StandardClassIds.Annotations.Java.Target -> { + /** + * This is required to avoid contract violation during [org.jetbrains.kotlin.fir.declarations.getDeprecationForCallSite] + * Because argument transformation may lead to [org.jetbrains.kotlin.fir.declarations.FirResolvePhase.TYPES]+ lazy resolution + * See KT-59342 + * TODO: KT-60520 + */ + argumentMapping = object : FirAnnotationArgumentMapping() { + override fun acceptChildren(visitor: FirVisitor, data: D) {} + override fun transformChildren(transformer: FirTransformer, data: D): FirElement = this + override val source: KtSourceElement? get() = null + + override val mapping: Map by lazy { + when { + classId == StandardClassIds.Annotations.Java.Target -> { when (val argument = arguments.firstOrNull()) { is JavaArrayAnnotationArgument -> argument.getElements().mapJavaTargetArguments(session) is JavaEnumValueAnnotationArgument -> listOf(argument).mapJavaTargetArguments(session) else -> null }?.let { - mapping[StandardClassIds.Annotations.ParameterNames.targetAllowedTargets] = it + mapOf(StandardClassIds.Annotations.ParameterNames.targetAllowedTargets to it) } } - StandardClassIds.Annotations.Java.Retention -> { + + classId == StandardClassIds.Annotations.Java.Retention -> { arguments.firstOrNull()?.mapJavaRetentionArgument(session)?.let { - mapping[StandardClassIds.Annotations.ParameterNames.retentionValue] = it + mapOf(StandardClassIds.Annotations.ParameterNames.retentionValue to it) } } - StandardClassIds.Annotations.Java.Deprecated -> { - mapping[StandardClassIds.Annotations.ParameterNames.deprecatedMessage] = - "Deprecated in Java".createConstantOrError(session) + + classId == StandardClassIds.Annotations.Java.Deprecated -> { + mapOf( + StandardClassIds.Annotations.ParameterNames.deprecatedMessage to "Deprecated in Java".createConstantOrError(session) + ) } - else -> { - if (lookupTag != null) { - fillAnnotationArgumentMapping(session, javaTypeParameterStack, lookupTag, arguments, mapping) - } + + lookupTag == null -> null + else -> arguments.ifNotEmpty { + val mapping = LinkedHashMap(size) + fillAnnotationArgumentMapping(session, lookupTag, this, mapping) + mapping } - } + }.orEmpty() } } }