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()
}
}
}