[FIR] make java annotation mapping lazily
to avoid contract violations from getDeprecation ^KT-59342 Fixed ^KT-60520
This commit is contained in:
committed by
Space Team
parent
4f92e7c48c
commit
32ad4380b3
@@ -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}
|
||||
}
|
||||
Vendored
+22
@@ -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?|
|
||||
|
||||
}
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user