From e625afc6a8ea664ddebf06b6b99bca103224b2d2 Mon Sep 17 00:00:00 2001 From: Jiaxiang Chen Date: Fri, 3 May 2024 16:41:37 -0700 Subject: [PATCH] fix annotation default value parsing for arrays and class literals. --- .../impl/symbol/java/KSAnnotationJavaImpl.kt | 10 +++- .../impl/symbol/kotlin/KSAnnotationImpl.kt | 14 ++++- .../impl/symbol/kotlin/KSValueArgumentImpl.kt | 4 +- .../devtools/ksp/impl/symbol/kotlin/util.kt | 56 ++++++++++++++++++- .../com/google/devtools/ksp/test/KSPAATest.kt | 2 - 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/java/KSAnnotationJavaImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/java/KSAnnotationJavaImpl.kt index f2ac92889b..d46c4bdf08 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/java/KSAnnotationJavaImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/java/KSAnnotationJavaImpl.kt @@ -96,9 +96,17 @@ class KSAnnotationJavaImpl private constructor(private val psi: PsiAnnotation, o (symbol.psi as PsiClass).allMethods.filterIsInstance() .mapNotNull { annoMethod -> annoMethod.defaultValue?.let { + val value = it + val calculatedValue: Any? = if (value is PsiArrayInitializerMemberValue) { + value.initializers.map { + calcValue(it) + } + } else { + calcValue(it) + } KSValueArgumentLiteImpl.getCached( KSNameImpl.getCached(annoMethod.name), - calcValue(it), + calculatedValue, Origin.SYNTHETIC ) } diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSAnnotationImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSAnnotationImpl.kt index c582557c30..a84c04b5d0 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSAnnotationImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSAnnotationImpl.kt @@ -26,6 +26,7 @@ import com.google.devtools.ksp.impl.symbol.java.calcValue import com.google.devtools.ksp.impl.symbol.kotlin.resolved.KSTypeReferenceResolvedImpl import com.google.devtools.ksp.symbol.* import com.intellij.psi.PsiAnnotationMethod +import com.intellij.psi.PsiArrayInitializerMemberValue import com.intellij.psi.PsiClass import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue @@ -76,10 +77,17 @@ class KSAnnotationImpl private constructor( if (symbol.origin == KtSymbolOrigin.JAVA && symbol.psi != null) { (symbol.psi as PsiClass).allMethods.filterIsInstance() .mapNotNull { annoMethod -> - annoMethod.defaultValue?.let { + annoMethod.defaultValue?.let { value -> + val calculatedValue: Any? = if (value is PsiArrayInitializerMemberValue) { + value.initializers.map { + calcValue(it) + } + } else { + calcValue(value) + } KSValueArgumentLiteImpl.getCached( KSNameImpl.getCached(annoMethod.name), - calcValue(it), + calculatedValue, Origin.SYNTHETIC ) } @@ -92,7 +100,7 @@ class KSAnnotationImpl private constructor( KtNamedAnnotationValue( valueParameterSymbol.name, constantValue, - KtAlwaysAccessibleLifetimeToken(ResolverAAImpl.ktModule.project!!) + KtAlwaysAccessibleLifetimeToken(ResolverAAImpl.ktModule.project) ), Origin.SYNTHETIC ) diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSValueArgumentImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSValueArgumentImpl.kt index 23e86432e3..f981f4fa7a 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSValueArgumentImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSValueArgumentImpl.kt @@ -78,9 +78,7 @@ class KSValueArgumentImpl private constructor( } ?: KSErrorType // TODO: handle local classes. is KtKClassAnnotationValue -> { - val classDeclaration = - (this@toValue.classId?.toKtClassSymbol())?.let { KSClassDeclarationImpl.getCached(it) } - classDeclaration?.asStarProjectedType() ?: KSErrorType + KSTypeImpl.getCached(this@toValue.type) } is KtConstantAnnotationValue -> this.constantValue.value is KtUnsupportedAnnotationValue -> null diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/util.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/util.kt index 355d11abe4..55be27508b 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/util.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/util.kt @@ -16,6 +16,8 @@ */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@file:OptIn(KtAnalysisApiInternals::class, KtAnalysisApiInternals::class) + package com.google.devtools.ksp.impl.symbol.kotlin import com.google.devtools.ksp.ExceptionMessage @@ -29,6 +31,7 @@ import com.google.devtools.ksp.impl.symbol.util.getDocString import com.google.devtools.ksp.symbol.* import com.intellij.psi.PsiElement import com.intellij.psi.PsiJavaFile +import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance @@ -38,6 +41,7 @@ import org.jetbrains.kotlin.analysis.api.annotations.* import org.jetbrains.kotlin.analysis.api.components.KtSubstitutorBuilder import org.jetbrains.kotlin.analysis.api.components.buildClassType import org.jetbrains.kotlin.analysis.api.components.buildTypeParameterType +import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirValueParameterSymbol import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType @@ -50,9 +54,20 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.java.JavaVisibilities +import org.jetbrains.kotlin.fir.declarations.getTargetType +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirArrayLiteral +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack import org.jetbrains.kotlin.fir.java.toFirExpression +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeErrorType +import org.jetbrains.kotlin.fir.types.ConeFlexibleType +import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType +import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.isAny import org.jetbrains.kotlin.load.java.structure.JavaAnnotationArgument import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl @@ -416,6 +431,43 @@ internal inline fun KSNode.findParentOfType(): KSNode? { @OptIn(SymbolInternals::class) internal fun KtValueParameterSymbol.getDefaultValue(): KtAnnotationValue? { + fun FirExpression.toValue(builder: KtSymbolByFirBuilder): KtAnnotationValue? { + if (this is FirAnnotation) { + return KtAnnotationApplicationValue( + KtAnnotationApplicationWithArgumentsInfo( + ClassId.fromString((annotationTypeRef.coneType as? ConeLookupTagBasedType)?.lookupTag.toString()), + null, + null, + emptyList(), + 0, + null, + KtAlwaysAccessibleLifetimeToken(ResolverAAImpl.ktModule.project) + ), + KtAlwaysAccessibleLifetimeToken(ResolverAAImpl.ktModule.project) + ) + } + if (this is FirArrayLiteral) { + return KtArrayAnnotationValue(argumentList.arguments.mapNotNull { it.toValue(builder) }, null, token) + } + return if (this is FirGetClassCall) { + var coneType = this.getTargetType()?.fullyExpandedType(builder.rootSession) + // FirAnnotationValueConverter expects fir type, while the type parsed from libraries are modeled + // as flexible type, for it to be used in argument of KClass values, it needs to be unwrapped. + if (coneType is ConeFlexibleType) { + coneType = coneType.lowerBound + } + + if (coneType is ConeClassLikeType && coneType !is ConeErrorType) { + val classId = coneType.lookupTag.classId + val type = builder.typeBuilder.buildKtType(coneType) + KtKClassAnnotationValue(type, classId, null, token) + } else { + null + } + } else { + FirAnnotationValueConverter.toConstantValue(this, builder) + } + } return this.psi.let { when (it) { is KtParameter -> analyze { @@ -461,9 +513,7 @@ internal fun KtValueParameterSymbol.getDefaultValue(): KtAnnotationValue? { val expectedTypeRef = it.firSymbol.fir.returnTypeRef val expression = defaultValue ?.toFirExpression(firSession, JavaTypeParameterStack.EMPTY, expectedTypeRef, null) - expression?.let { - FirAnnotationValueConverter.toConstantValue(expression, symbolBuilder) - } + expression?.toValue(symbolBuilder) } } else -> throw IllegalStateException("Unhandled default value type ${it.javaClass}") diff --git a/test-utils/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt b/test-utils/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt index 6585458eca..165c05e09c 100644 --- a/test-utils/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt +++ b/test-utils/src/test/kotlin/com/google/devtools/ksp/test/KSPAATest.kt @@ -65,7 +65,6 @@ class KSPAATest : AbstractKSPAATest() { runTest("../kotlin-analysis-api/testData/allFunctions_kt_inherits_java.kt") } - @Disabled @TestMetadata("annotationInDependencies.kt") @Test fun testAnnotationsInDependencies() { @@ -78,7 +77,6 @@ class KSPAATest : AbstractKSPAATest() { runTest("../test-utils/testData/api/annotationOnConstructorParameter.kt") } - @Disabled @TestMetadata("annotationWithArbitraryClassValue.kt") @Test fun testAnnotationWithArbitraryClassValue() {