[FIR] make java annotation mapping lazily

to avoid contract violations from getDeprecation

^KT-59342 Fixed
^KT-60520
This commit is contained in:
Dmitrii Gridin
2023-07-13 19:31:52 +02:00
committed by Space Team
parent 4f92e7c48c
commit 32ad4380b3
6 changed files with 140 additions and 33 deletions
@@ -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 {
<expr>private val branchManager: Manager? = null</expr>
}
// 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}
}
@@ -0,0 +1,22 @@
KT element: KtProperty
FIR element: FirPropertyImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
private final [ResolvedTo(BODY_RESOLVE)] val branchManager: <ERROR TYPE REF: Symbol not found for Manager?> = Null(null)
private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): <ERROR TYPE REF: Symbol not found for 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<R|kotlin/Any|>
}
private final [ResolvedTo(BODY_RESOLVE)] val branchManager: <ERROR TYPE REF: Symbol not found for Manager?> = Null(null)
private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): <ERROR TYPE REF: Symbol not found for Manager?>
}
@@ -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<R|kotlin/Any|>
}
private final [ResolvedTo(BODY_RESOLVE)] val branchManager: R|one/four/Manager?| = Null(null)
private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=KotlinFile] get(): R|one/four/Manager?|
}
@@ -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 {
@@ -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 {
@@ -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<JavaAnnotation>.convertAnnotationsToFir(
@@ -210,10 +216,9 @@ private fun JavaAnnotationArgument.mapJavaRetentionArgument(session: FirSession)
private fun fillAnnotationArgumentMapping(
session: FirSession,
javaTypeParameterStack: JavaTypeParameterStack,
lookupTag: ConeClassLikeLookupTagImpl,
annotationArguments: Collection<JavaAnnotationArgument>,
destination: MutableMap<Name, FirExpression>
destination: MutableMap<Name, FirExpression>,
) {
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 <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement = this
override val source: KtSourceElement? get() = null
override val mapping: Map<Name, FirExpression> 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<Name, FirExpression>(size)
fillAnnotationArgumentMapping(session, lookupTag, this, mapping)
mapping
}
}
}.orEmpty()
}
}
}