Analysis API: separate constant values from annotation values

This commit is contained in:
Ilya Kirillov
2021-11-23 20:44:39 +01:00
parent 47c1da2845
commit f722a54c78
53 changed files with 302 additions and 415 deletions
@@ -8,8 +8,7 @@ package org.jetbrains.kotlin.analysis.api.fir
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplication
import org.jetbrains.kotlin.analysis.api.fir.annotations.fullyExpandedClassId
import org.jetbrains.kotlin.analysis.api.fir.annotations.mapAnnotationParameters
import org.jetbrains.kotlin.analysis.api.fir.evaluate.KtFirConstantValueConverter
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirImplicitInvokeCall
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.util.OperatorNameConventions
/**
@@ -80,7 +78,7 @@ internal fun FirAnnotation.toKtAnnotationApplication(useSiteSession: FirSession)
fullyExpandedClassId(useSiteSession),
psi as? KtAnnotationEntry,
useSiteTarget,
KtFirConstantValueConverter.toNamedConstantValue(
FirAnnotationValueConverter.toNamedConstantValue(
mapAnnotationParameters(this, useSiteSession),
useSiteSession,
)
@@ -5,18 +5,18 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationValue
import org.jetbrains.kotlin.analysis.api.annotations.KtErrorValue
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEvaluator
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
import org.jetbrains.kotlin.analysis.api.components.KtCompileTimeConstantProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.evaluate.KtFirConstantValueConverter
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEvaluator
import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken
import org.jetbrains.kotlin.analysis.api.withValidityAssertion
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenBranch
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
internal class KtFirCompileTimeConstantProvider(
@@ -24,14 +24,13 @@ internal class KtFirCompileTimeConstantProvider(
override val token: ValidityToken,
) : KtCompileTimeConstantProvider(), KtFirAnalysisSessionComponent {
override fun evaluate(expression: KtExpression): KtAnnotationValue? = withValidityAssertion {
override fun evaluate(expression: KtExpression): KtConstantValue? = withValidityAssertion {
when (val fir = expression.getOrBuildFir(firResolveState)) {
is FirExpression -> {
try {
FirCompileTimeConstantEvaluator.evaluate(fir)?.let { KtFirConstantValueConverter.toConstantValue(it) }
?: KtFirConstantValueConverter.toConstantValue(fir, firResolveState.rootModuleSession)
FirCompileTimeConstantEvaluator.evaluateAsKtConstantExpression(fir)
} catch (e: ArithmeticException) {
KtErrorValue(e.localizedMessage)
KtConstantValue.KtErrorConstantValue(e.localizedMessage, fir.psi as? KtElement)
}
}
// For invalid code like the following,
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.fir.evaluate
import org.jetbrains.kotlin.analysis.api.annotations.*
import org.jetbrains.kotlin.analysis.api.base.KtConstantValueFactory
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
@@ -20,8 +21,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.ArrayFqNames
internal object KtFirConstantValueConverter {
internal object FirAnnotationValueConverter {
fun toNamedConstantValue(
argumentMapping: Map<String, FirExpression>,
session: FirSession,
@@ -33,13 +33,10 @@ internal object KtFirConstantValueConverter {
)
}
fun <T> toConstantValue(
firConstExpression: FirConstExpression<T>,
): KtLiteralAnnotationValue<T> =
firConstExpression.convertConstantExpression()
private fun <T> FirConstExpression<T>.convertConstantExpression(): KtLiteralAnnotationValue<T> =
KtLiteralAnnotationValue(kind, value, psi as? KtElement)
private fun <T> FirConstExpression<T>.convertConstantExpression(): KtConstantAnnotationValue? {
val constantValue = KtConstantValueFactory.createConstantValue(value, psi as? KtElement) ?: return null
return KtConstantAnnotationValue(constantValue)
}
private fun Collection<FirExpression>.convertConstantExpression(
session: FirSession,
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.analysis.api.fir.evaluate
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationValue
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
import org.jetbrains.kotlin.analysis.api.base.KtConstantValueFactory
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
@@ -14,10 +15,12 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.argument
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.constants.evaluate.CompileTimeType
import org.jetbrains.kotlin.resolve.constants.evaluate.evalBinaryOp
import org.jetbrains.kotlin.resolve.constants.evaluate.evalUnaryOp
@@ -28,7 +31,6 @@ import org.jetbrains.kotlin.types.ConstantValueKind
* and the argument, are compile-time constant as well.
*/
internal object FirCompileTimeConstantEvaluator {
// TODO: Handle boolean operators, const property loading, class reference, array, annotation values, etc.
fun evaluate(expression: FirExpression): FirConstExpression<*>? =
when (expression) {
@@ -37,9 +39,9 @@ internal object FirCompileTimeConstantEvaluator {
else -> null
}
fun evaluateAsKtConstantExpression(expression: FirExpression): KtAnnotationValue? {
fun evaluateAsKtConstantExpression(expression: FirExpression): KtConstantValue? {
val evaluated = evaluate(expression) ?: return null
return KtFirConstantValueConverter.toConstantValue(evaluated)
return KtConstantValueFactory.createConstantValue(evaluated.value, evaluated.psi as? KtElement)
}
// TODO: Rework to handle nested expressions
@@ -106,7 +108,7 @@ internal object FirCompileTimeConstantEvaluator {
kind.toCompileTimeType(),
kind.convertToNumber(value as? Number)!!
)?.let {
it.toConstantValueKind()?.toConstExpression(source, it)
it.toConstantValueKind().toConstExpression(source, it)
}
}
@@ -123,7 +125,7 @@ internal object FirCompileTimeConstantEvaluator {
other.kind.toCompileTimeType(),
other.kind.convertToNumber(other.value as? Number)!!
)?.let {
it.toConstantValueKind()?.toConstExpression(source, it)
it.toConstantValueKind().toConstExpression(source, it)
}
}
@@ -176,7 +178,7 @@ internal object FirCompileTimeConstantEvaluator {
else -> null
}
private fun <T : Any> T.toConstantValueKind(): ConstantValueKind<*>? =
private fun <T> T.toConstantValueKind(): ConstantValueKind<*> =
when (this) {
is Byte -> ConstantValueKind.Byte
is Double -> ConstantValueKind.Double
@@ -189,7 +191,8 @@ internal object FirCompileTimeConstantEvaluator {
is String -> ConstantValueKind.String
is Boolean -> ConstantValueKind.Boolean
else -> null
null -> ConstantValueKind.Null
else -> error("Unknown constant value")
}
private fun ConstantValueKind<*>.convertToNumber(value: Number?): Number? {
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.analysis.api.fir.renderer
import org.jetbrains.kotlin.analysis.api.fir.annotations.mapAnnotationParameters
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEvaluator
import org.jetbrains.kotlin.analysis.api.fir.evaluate.KtFirConstantValueConverter
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtConstantValueRenderer
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationValueRenderer
import org.jetbrains.kotlin.analysis.api.annotations.KtUnsupportedAnnotationValue
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
@@ -57,8 +57,8 @@ private fun renderAndSortAnnotationArguments(descriptor: FirAnnotation, session:
private fun renderConstant(value: FirExpression, useSiteSession: FirSession): String {
val evaluated = FirCompileTimeConstantEvaluator.evaluate(value)
val constantValue = KtFirConstantValueConverter.toConstantValue(evaluated ?: value, useSiteSession)
val constantValue = FirAnnotationValueConverter.toConstantValue(evaluated ?: value, useSiteSession)
?: KtUnsupportedAnnotationValue
return KtConstantValueRenderer.render(constantValue)
return KtAnnotationValueRenderer.render(constantValue)
}
@@ -24,54 +24,6 @@ public class FirCompileTimeConstantEvaluatorTestGenerated extends AbstractFirCom
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("annotationInAnnotation_arrayOf.kt")
public void testAnnotationInAnnotation_arrayOf() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_arrayOf.kt");
}
@Test
@TestMetadata("annotationInAnnotation_collectionLiteral.kt")
public void testAnnotationInAnnotation_collectionLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_collectionLiteral.kt");
}
@Test
@TestMetadata("annotationInAnnotation_collectionLiteral_spread.kt")
public void testAnnotationInAnnotation_collectionLiteral_spread() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_collectionLiteral_spread.kt");
}
@Test
@TestMetadata("annotationInAnnotation_multipleAnnotations_arrayOf.kt")
public void testAnnotationInAnnotation_multipleAnnotations_arrayOf() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_multipleAnnotations_arrayOf.kt");
}
@Test
@TestMetadata("annotationInAnnotation_multipleAnnotations_collectionLiteral.kt")
public void testAnnotationInAnnotation_multipleAnnotations_collectionLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt");
}
@Test
@TestMetadata("annotationInAnnotation_single.kt")
public void testAnnotationInAnnotation_single() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_single.kt");
}
@Test
@TestMetadata("annotationInAnnotation_vararg.kt")
public void testAnnotationInAnnotation_vararg() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/annotationInAnnotation_vararg.kt");
}
@Test
@TestMetadata("enumAsAnnotationValue.kt")
public void testEnumAsAnnotationValue() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/enumAsAnnotationValue.kt");
}
@Test
@TestMetadata("propertyInit_Byte.kt")
public void testPropertyInit_Byte() throws Exception {