FIR IDE: more comprehensive abstractions of annotation values

This commit is contained in:
Jinseong Jeon
2021-09-16 12:04:28 -07:00
committed by Ilya Kirillov
parent fe41c4513f
commit 6ef2dad895
36 changed files with 316 additions and 52 deletions
@@ -84,7 +84,6 @@ internal class KtSymbolByFirBuilder private constructor(
filesCache = BuilderCache(),
)
fun createReadOnlyCopy(newResolveState: FirModuleResolveState): KtSymbolByFirBuilder {
check(!withReadOnlyCaching) { "Cannot create readOnly KtSymbolByFirBuilder from a readonly one" }
return KtSymbolByFirBuilder(
@@ -99,7 +98,6 @@ internal class KtSymbolByFirBuilder private constructor(
)
}
fun buildSymbol(fir: FirDeclaration): KtSymbol {
return when (fir) {
is FirClassLikeDeclaration -> classifierBuilder.buildClassLikeSymbol(fir)
@@ -109,11 +107,9 @@ internal class KtSymbolByFirBuilder private constructor(
}
}
fun buildEnumEntrySymbol(fir: FirEnumEntry) = symbolsCache.cache(fir) { KtFirEnumEntrySymbol(fir, resolveState, token, this) }
fun buildFileSymbol(fir: FirFile) = filesCache.cache(fir) { KtFirFileSymbol(fir, resolveState, token) }
fun buildFileSymbol(fir: FirFile) = filesCache.cache(fir) { KtFirFileSymbol(fir, resolveState, token, this) }
private val packageProvider = project.createPackageProvider(GlobalSearchScope.allScope(project))//todo scope
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirEle
import org.jetbrains.kotlin.analysis.api.components.KtCompileTimeConstantProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.utils.convertConstantExpression
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSimpleConstantValue
import org.jetbrains.kotlin.analysis.api.symbols.markers.*
import org.jetbrains.kotlin.analysis.api.tokens.ValidityToken
import org.jetbrains.kotlin.analysis.api.withValidityAssertion
import org.jetbrains.kotlin.psi.KtExpression
@@ -22,9 +22,12 @@ internal class KtFirCompileTimeConstantProvider(
override val token: ValidityToken,
) : KtCompileTimeConstantProvider(), KtFirAnalysisSessionComponent {
override fun evaluate(expression: KtExpression): KtSimpleConstantValue<*>? = withValidityAssertion {
override fun evaluate(expression: KtExpression): KtConstantValue? = withValidityAssertion {
when (val fir = expression.getOrBuildFir(firResolveState)) {
is FirExpression -> FirCompileTimeConstantEvaluator().evaluate(fir)?.convertConstantExpression()
is FirExpression -> {
FirCompileTimeConstantEvaluator().evaluate(fir)?.convertConstantExpression()
?: fir.convertConstantExpression(firResolveState.rootModuleSession, firSymbolBuilder)
}
else -> throwUnexpectedFirElementError(fir, expression)
}
}
@@ -40,7 +40,7 @@ internal class FirCompileTimeConstantEvaluator {
// This is no longer used during FIR2IR where an inner expression is recursively rewritten to ConstExpression if possible.
// Maybe rewrite this to a recursive version with caching either here or in provider.
private fun evaluate(functionCall: FirFunctionCall): FirConstExpression<*>? {
val function = functionCall.getOriginalFunction()!! as FirSimpleFunction
val function = functionCall.getOriginalFunction()!! as? FirSimpleFunction ?: return null
val opr1 = functionCall.explicitReceiver as? FirConstExpression<*> ?: return null
opr1.evaluate(function)?.let {
@@ -35,7 +35,7 @@ internal class KtFirAnonymousObjectSymbol(
override val firRef = firRef(fir, resolveState)
override val psi: PsiElement? by firRef.withFirAndCache { fir -> fir.findPsi(fir.moduleData.session) }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -59,7 +59,7 @@ internal class KtFirConstructorSymbol(
override val visibility: Visibility get() = getVisibility()
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.fir.symbols
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.analysis.api.fir.findPsi
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
@@ -26,6 +27,7 @@ internal class KtFirFileSymbol(
fir: FirFile,
resolveState: FirModuleResolveState,
override val token: ValidityToken,
_builder: KtSymbolByFirBuilder,
) : KtFileSymbol(), KtSymbolWithDeclarations, KtFirSymbol<FirFile> {
override val firRef = firRef(fir, resolveState)
override val psi: PsiElement? by firRef.withFirAndCache { fir -> fir.findPsi(fir.moduleData.session) }
@@ -35,7 +37,7 @@ internal class KtFirFileSymbol(
TODO("Creating pointers for files from library is not supported yet")
}
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -64,7 +64,7 @@ internal class KtFirFunctionSymbol(
override val hasStableParameterNames: Boolean = firRef.withFir { it.getHasStableParameterNames(it.moduleData.session) }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -71,7 +71,9 @@ internal class KtFirKotlinPropertySymbol(
}
override val isExtension: Boolean get() = firRef.withFir { it.receiverTypeRef != null }
override val initializer: KtConstantValue? by firRef.withFirAndCache(FirResolvePhase.BODY_RESOLVE) { fir -> fir.initializer?.convertConstantExpression() }
override val initializer: KtConstantValue? by firRef.withFirAndCache(FirResolvePhase.BODY_RESOLVE) { fir ->
fir.initializer?.convertConstantExpression(resolveState.rootModuleSession, _builder)
}
override val symbolKind: KtSymbolKind
get() = firRef.withFir { fir ->
when (fir.containingClass()?.classId) {
@@ -83,7 +85,7 @@ internal class KtFirKotlinPropertySymbol(
override val visibility: Visibility get() = getVisibility()
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -69,7 +69,7 @@ internal class KtFirNamedClassOrObjectSymbol(
else -> possiblyRawVisibility
}
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -59,7 +59,7 @@ internal class KtFirPropertyGetterSymbol(
}
override val modality: Modality get() = getModality()
override val visibility: Visibility get() = getVisibility()
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -57,7 +57,7 @@ internal class KtFirPropertySetterSymbol(
override val modality: Modality get() = getModality()
override val visibility: Visibility get() = getVisibility()
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -59,12 +59,14 @@ internal class KtFirSyntheticJavaPropertySymbol(
firRef.receiverTypeAndAnnotations(builder)
}
override val isExtension: Boolean get() = firRef.withFir { it.receiverTypeRef != null }
override val initializer: KtConstantValue? by firRef.withFirAndCache(FirResolvePhase.BODY_RESOLVE) { fir -> fir.initializer?.convertConstantExpression() }
override val initializer: KtConstantValue? by firRef.withFirAndCache(FirResolvePhase.BODY_RESOLVE) { fir ->
fir.initializer?.convertConstantExpression(resolveState.rootModuleSession, _builder)
}
override val modality: Modality get() = getModality()
override val visibility: Visibility get() = getVisibility()
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -37,7 +37,7 @@ internal class KtFirTypeAndAnnotations<T : FirDeclaration>(
override val annotations: List<KtAnnotationCall> by containingDeclaration.withFirAndCache { fir ->
typeRef(fir).annotations.map {
KtFirAnnotationCall(containingDeclaration, it)
KtFirAnnotationCall(containingDeclaration, it, _builder)
}
}
}
@@ -79,7 +79,7 @@ private fun List<FirTypeRef>.mapToTypeAndAnnotations(
builder: KtSymbolByFirBuilder,
) = map { typeRef ->
val annotations = typeRef.annotations.map { annotation ->
KtFirAnnotationCall(containingDeclaration, annotation)
KtFirAnnotationCall(containingDeclaration, annotation, builder)
}
KtSimpleFirTypeAndAnnotations(typeRef.coneType, annotations, builder, containingDeclaration.token)
}
@@ -45,7 +45,7 @@ internal class KtFirValueParameterSymbol(
override val annotatedType: KtTypeAndAnnotations by firRef.withFirAndCache(FirResolvePhase.TYPES) { fir ->
if (fir.isVararg) {
val annotations = fir.returnTypeRef.annotations.map { annotation ->
KtFirAnnotationCall(firRef, annotation)
KtFirAnnotationCall(firRef, annotation, _builder)
}
// There SHOULD always be an array element type (even if it is an error type, e.g., unresolved).
val arrayElementType = fir.returnTypeRef.coneType.arrayElementType()
@@ -58,7 +58,7 @@ internal class KtFirValueParameterSymbol(
override val hasDefaultValue: Boolean get() = firRef.withFir { it.defaultValue != null }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList() }
override val annotations: List<KtAnnotationCall> by cached { firRef.toAnnotationsList(_builder) }
override fun containsAnnotation(classId: ClassId): Boolean = firRef.containsAnnotation(classId)
override val annotationClassIds: Collection<ClassId> by cached { firRef.getAnnotationClassIds() }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.analysis.api.fir.symbols.annotations
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
@@ -19,7 +20,8 @@ import org.jetbrains.kotlin.psi.KtCallElement
internal class KtFirAnnotationCall(
private val containingDeclaration: FirRefWithValidityCheck<FirDeclaration>,
annotation: FirAnnotation
annotation: FirAnnotation,
_builder: KtSymbolByFirBuilder,
) : KtAnnotationCall() {
private val annotationCallRef by weakRef(annotation)
@@ -39,9 +41,7 @@ internal class KtFirAnnotationCall(
override val useSiteTarget: AnnotationUseSiteTarget? get() = annotationCallRef.useSiteTarget
override val arguments: List<KtNamedConstantValue> by containingDeclaration.withFirAndCache(ResolveType.AnnotationsArguments) { fir ->
mapAnnotationParameters(annotationCallRef, fir.moduleData.session).map { (name, expression) ->
KtNamedConstantValue(name, expression.convertConstantExpression())
}
mapAnnotationParameters(annotationCallRef, fir.moduleData.session).toNamedConstantValue(fir.moduleData.session, _builder)
}
override fun equals(other: Any?): Boolean {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.analysis.api.fir.symbols.annotations
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
@@ -15,12 +16,13 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.api.fir.utils.FirRefWithValidityCheck
import org.jetbrains.kotlin.name.ClassId
internal fun FirAnnotation.getClassId(session: FirSession): ClassId? =
coneClassLikeType?.fullyExpandedType(session)?.classId
internal fun FirRefWithValidityCheck<FirAnnotatedDeclaration>.toAnnotationsList() = withFir { fir ->
fir.annotations.map { KtFirAnnotationCall(this, it) }
internal fun FirRefWithValidityCheck<FirAnnotatedDeclaration>.toAnnotationsList(
builder: KtSymbolByFirBuilder
) = withFir { fir ->
fir.annotations.map { KtFirAnnotationCall(this, it, builder) }
}
internal fun FirRefWithValidityCheck<FirAnnotatedDeclaration>.containsAnnotation(classId: ClassId): Boolean =
@@ -5,14 +5,12 @@
package org.jetbrains.kotlin.analysis.api.fir.utils
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder
import org.jetbrains.kotlin.analysis.api.fir.buildSymbol
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.resolved
import org.jetbrains.kotlin.fir.declarations.utils.primaryConstructor
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
@@ -24,15 +22,18 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
import org.jetbrains.kotlin.analysis.api.symbols.KtEnumEntrySymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.withFirDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtConstantValue
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSimpleConstantValue
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtUnsupportedConstantValue
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.ArrayFqNames
internal fun PsiElement.unwrap(): PsiElement {
return when (this) {
@@ -96,13 +97,83 @@ internal fun mapAnnotationParameters(annotation: FirAnnotation, session: FirSess
return resultSet
}
internal fun Map<String, FirExpression>.toNamedConstantValue(
session: FirSession,
firSymbolBuilder: KtSymbolByFirBuilder
): List<KtNamedConstantValue> =
map { (name, expression) ->
KtNamedConstantValue(
name,
expression.convertConstantExpression(session, firSymbolBuilder) ?: KtUnsupportedConstantValue
)
}
internal fun <T> FirConstExpression<T>.convertConstantExpression(): KtSimpleConstantValue<T> = KtSimpleConstantValue(kind, value)
internal fun FirExpression.convertConstantExpression(): KtConstantValue =
when (this) {
private fun Collection<FirExpression>.convertConstantExpression(
session: FirSession,
firSymbolBuilder: KtSymbolByFirBuilder
): Collection<KtConstantValue> =
mapNotNull { it.convertConstantExpression(session, firSymbolBuilder) }
private fun Collection<KtConstantValue>.toArrayConstantValueIfNecessary(): KtConstantValue {
return if (size == 1)
single()
else
KtArrayConstantValue(this)
}
internal fun FirExpression.convertConstantExpression(
session: FirSession,
firSymbolBuilder: KtSymbolByFirBuilder
): KtConstantValue? {
return when (this) {
is FirConstExpression<*> -> convertConstantExpression()
is FirNamedArgumentExpression -> {
expression.convertConstantExpression(session, firSymbolBuilder)
}
is FirVarargArgumentsExpression -> {
arguments.convertConstantExpression(session, firSymbolBuilder).toArrayConstantValueIfNecessary()
}
is FirArrayOfCall -> {
argumentList.arguments.convertConstantExpression(session, firSymbolBuilder).toArrayConstantValueIfNecessary()
}
is FirFunctionCall -> {
val reference = calleeReference as? FirResolvedNamedReference ?: return null
when (val resolvedSymbol = reference.resolvedSymbol) {
is FirConstructorSymbol -> {
val classSymbol = resolvedSymbol.getContainingClassSymbol(session) ?: return null
if ((classSymbol.fir as? FirClass)?.classKind == ClassKind.ANNOTATION_CLASS) {
val resultMap = mutableMapOf<String, FirExpression>()
argumentMapping?.entries?.forEach { (arg, param) ->
resultMap[param.name.asString()] = arg
}
KtAnnotationConstantValue(
resolvedSymbol.callableId.className?.asString(),
resultMap.toNamedConstantValue(session, firSymbolBuilder)
)
} else null
}
is FirNamedFunctionSymbol -> {
if (resolvedSymbol.callableId.asSingleFqName() in ArrayFqNames.ARRAY_CALL_FQ_NAMES)
argumentList.arguments.convertConstantExpression(session, firSymbolBuilder).toArrayConstantValueIfNecessary()
else null
}
else -> null
}
}
is FirPropertyAccessExpression -> {
val reference = calleeReference as? FirResolvedNamedReference ?: return null
when (val resolvedSymbol = reference.resolvedSymbol) {
is FirEnumEntrySymbol -> {
KtEnumEntryValue(resolvedSymbol.fir.buildSymbol(firSymbolBuilder) as KtEnumEntrySymbol)
}
else -> null
}
}
else -> KtUnsupportedConstantValue
}
}
internal fun KtTypeNullability.toConeNullability() = when (this) {
KtTypeNullability.NULLABLE -> ConeNullability.NULLABLE
@@ -9,23 +9,62 @@ import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.symbols.markers.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractCompileTimeConstantEvaluatorTest : AbstractHLApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val expression = testServices.expressionMarkerProvider.getSelectedElement(ktFile) as KtExpression
val element = testServices.expressionMarkerProvider.getSelectedElement(ktFile)
val expression = when (element) {
is KtExpression -> element
is KtValueArgument -> element.getArgumentExpression()
else -> null
} ?: testServices.assertions.fail { "Unsupported expression: $element" }
val constantValue = executeOnPooledThreadInReadAction {
analyse(expression) { expression.evaluate() }
}
val actual = buildString {
appendLine("expression: ${expression.text}")
appendLine("constant_value: $constantValue")
appendLine("constant: ${constantValue?.toConst()}")
appendLine("constant_value: ${analyse(ktFile) { constantValue?.stringRepresentation() }}")
appendLine("constant: ${(constantValue as? KtSimpleConstantValue<*>)?.toConst()}")
}
testServices.assertions.assertEqualsToFile(testDataFileSibling(".txt"), actual)
}
private fun KtConstantValue.stringRepresentation(): String {
return when (this) {
is KtArrayConstantValue -> buildString {
appendLine("KtArrayConstantValue [")
appendLine(INDENT, values.joinToString(separator = "\n") { it.stringRepresentation() })
append("]")
}
is KtAnnotationConstantValue -> buildString {
append("KtAnnotationConstantValue(")
append(fqName)
append(", ")
arguments.joinTo(this, separator = ", ", prefix = "(", postfix = ")") {
"${it.name} = ${it.expression.stringRepresentation()}"
}
append(")")
}
is KtEnumEntryValue -> buildString {
append("KtEnumEntryValue(")
append(enumEntrySymbol.callableIdIfNonLocal ?: enumEntrySymbol.name)
append(")")
}
is KtSimpleConstantValue<*> -> toString()
is KtUnsupportedConstantValue -> "KtUnsupportedConstantValue"
}
}
private fun StringBuilder.appendLine(indent: Int, value: String) {
appendLine(value.prependIndent(" ".repeat(indent)))
}
}
private const val INDENT = 2
@@ -24,6 +24,36 @@ public class CompileTimeConstantEvaluatorTestGenerated extends AbstractCompileTi
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/compileTimeConstantEvaluator"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("annotationInAnnotation_arrayOf.kt")
public void testAnnotationInAnnotation_arrayOf() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/annotationInAnnotation_arrayOf.kt");
}
@Test
@TestMetadata("annotationInAnnotation_collectionLiteral.kt")
public void testAnnotationInAnnotation_collectionLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/annotationInAnnotation_collectionLiteral.kt");
}
@Test
@TestMetadata("annotationInAnnotation_multipleAnnotations_arrayOf.kt")
public void testAnnotationInAnnotation_multipleAnnotations_arrayOf() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/annotationInAnnotation_multipleAnnotations_arrayOf.kt");
}
@Test
@TestMetadata("annotationInAnnotation_multipleAnnotations_collectionLiteral.kt")
public void testAnnotationInAnnotation_multipleAnnotations_collectionLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/annotationInAnnotation_multipleAnnotations_collectionLiteral.kt");
}
@Test
@TestMetadata("enumAsAnnotationValue.kt")
public void testEnumAsAnnotationValue() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/enumAsAnnotationValue.kt");
}
@Test
@TestMetadata("propertyInit_Byte.kt")
public void testPropertyInit_Byte() throws Exception {
@@ -59,4 +89,10 @@ public class CompileTimeConstantEvaluatorTestGenerated extends AbstractCompileTi
public void testPropertyInit_UInt() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/propertyInit_UInt.kt");
}
@Test
@TestMetadata("stringLiteral.kt")
public void testStringLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantEvaluator/stringLiteral.kt");
}
}
@@ -5,14 +5,14 @@
package org.jetbrains.kotlin.analysis.api.components
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSimpleConstantValue
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtConstantValue
import org.jetbrains.kotlin.psi.KtExpression
public abstract class KtCompileTimeConstantProvider : KtAnalysisSessionComponent() {
public abstract fun evaluate(expression: KtExpression): KtSimpleConstantValue<*>?
public abstract fun evaluate(expression: KtExpression): KtConstantValue?
}
public interface KtCompileTimeConstantProviderMixIn : KtAnalysisSessionMixIn {
public fun KtExpression.evaluate(): KtSimpleConstantValue<*>? =
public fun KtExpression.evaluate(): KtConstantValue? =
analysisSession.compileTimeConstantProvider.evaluate(this)
}
@@ -87,6 +87,7 @@ public object DebugSymbolRenderer {
is KtFunctionSymbol -> renderValue(value.callableIdIfNonLocal ?: "<local>/${value.name}")
is KtSamConstructorSymbol -> renderValue(value.callableIdIfNonLocal ?: "<local>/${value.name}")
is KtConstructorSymbol -> "<constructor>"
is KtEnumEntrySymbol -> renderValue(value.callableIdIfNonLocal ?: "<local>/${value.name}")
is KtNamedSymbol -> renderValue(value.name)
is KtPropertyGetterSymbol -> "<getter>"
is KtPropertySetterSymbol -> "<setter>"
@@ -95,6 +96,7 @@ public object DebugSymbolRenderer {
"${value::class.simpleName!!}($symbolTag)"
}
is KtSimpleConstantValue<*> -> renderValue(value.value)
is KtEnumEntryValue -> "KtEnumEntryValue(${renderValue(value.enumEntrySymbol)})"
is KtNamedConstantValue -> "${renderValue(value.name)} = ${renderValue(value.expression)}"
is KtAnnotationCall -> buildString {
append(renderValue(value.classId))
@@ -5,11 +5,40 @@
package org.jetbrains.kotlin.analysis.api.symbols.markers
import org.jetbrains.kotlin.analysis.api.symbols.KtEnumEntrySymbol
import org.jetbrains.kotlin.types.ConstantValueKind
/**
* Annotation values are expected to be compile-time constants. According to the
* [spec](https://kotlinlang.org/spec/annotations.html#annotation-values),
* allowed kinds are:
* * integer types,
* * string type,
* * enum types,
* * other annotation types, and
* * array of aforementioned types
*
* [KtSimpleConstantValue] covers first two kinds;
* [KtEnumEntryValue] corresponds to enum types;
* [KtAnnotationConstantValue] represents annotation types (with annotation fq name and arguments); and
* [KtArrayConstantValue] abstracts an array of [KtConstantValue]s.
*/
public sealed class KtConstantValue
public object KtUnsupportedConstantValue : KtConstantValue()
public class KtArrayConstantValue(
public val values: Collection<KtConstantValue>
) : KtConstantValue()
public class KtAnnotationConstantValue(
public val fqName: String?,
public val arguments: List<KtNamedConstantValue>
) : KtConstantValue()
public class KtEnumEntryValue(
public val enumEntrySymbol: KtEnumEntrySymbol
) : KtConstantValue()
public data class KtSimpleConstantValue<T>(val constantValueKind: ConstantValueKind<T>, val value: T) : KtConstantValue() {
public fun toConst(): Any? {
return (value as? Long)?.let {
@@ -0,0 +1,6 @@
annotation class Annotation(vararg val strings: String)
annotation class AnnotationInner(val value: Annotation)
@AnnotationInner(<expr>Annotation(strings = arrayOf("v1", "v2"))</expr>)
class C
@@ -0,0 +1,6 @@
expression: Annotation(strings = arrayOf("v1", "v2"))
constant_value: KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v1)
KtSimpleConstantValue(constantValueKind=String, value=v2)
]))
constant: null
@@ -0,0 +1,6 @@
annotation class Annotation(vararg val strings: String)
annotation class AnnotationInner(val value: Annotation)
@AnnotationInner(<expr>Annotation(["v1", "v2"])</expr>)
class C
@@ -0,0 +1,6 @@
expression: Annotation(["v1", "v2"])
constant_value: KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v1)
KtSimpleConstantValue(constantValueKind=String, value=v2)
]))
constant: null
@@ -0,0 +1,6 @@
annotation class Annotation(vararg val strings: String)
annotation class AnnotationArray(vararg val annos: Annotation)
@AnnotationArray(<expr>annos = arrayOf(Annotation("v1", "v2"), Annotation(strings = arrayOf("v3", "v4")))</expr>)
class C
@@ -0,0 +1,12 @@
expression: arrayOf(Annotation("v1", "v2"), Annotation(strings = arrayOf("v3", "v4")))
constant_value: KtArrayConstantValue [
KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v1)
KtSimpleConstantValue(constantValueKind=String, value=v2)
]))
KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v3)
KtSimpleConstantValue(constantValueKind=String, value=v4)
]))
]
constant: null
@@ -0,0 +1,6 @@
annotation class Annotation(vararg val strings: String)
annotation class AnnotationArray(vararg val annos: Annotation)
@AnnotationArray(<expr>[Annotation("v1", "v2"), Annotation(["v3", "v4"])]</expr>)
class C
@@ -0,0 +1,12 @@
expression: [Annotation("v1", "v2"), Annotation(["v3", "v4"])]
constant_value: KtArrayConstantValue [
KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v1)
KtSimpleConstantValue(constantValueKind=String, value=v2)
]))
KtAnnotationConstantValue(Annotation, (strings = KtArrayConstantValue [
KtSimpleConstantValue(constantValueKind=String, value=v3)
KtSimpleConstantValue(constantValueKind=String, value=v4)
]))
]
constant: null
@@ -0,0 +1,10 @@
enum class Color {
R,
G,
B
}
annotation class Annotation(val color : Color)
@Annotation(<expr>Color.R</expr>)
class C
@@ -0,0 +1,3 @@
expression: Color.R
constant_value: KtEnumEntryValue(/Color.R)
constant: null
@@ -0,0 +1,4 @@
annotation class Annotation(vararg val values: String)
@Annotation(<expr>"42"</expr>)
class C
@@ -0,0 +1,3 @@
expression: "42"
constant_value: KtSimpleConstantValue(constantValueKind=String, value=42)
constant: 42
@@ -296,7 +296,7 @@ KtFirKotlinPropertySymbol:
kotlin/Deprecated
]
annotations: [
kotlin/Deprecated(level = KtUnsupportedConstantValue, message = don't use j)
kotlin/Deprecated(level = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/DeprecationLevel.ERROR)), message = don't use j)
psi: KtAnnotationEntry
]
callableIdIfNonLocal: /j
@@ -332,7 +332,7 @@ KtFirKotlinPropertySymbol:
kotlin/Deprecated
]
annotations: [
kotlin/Deprecated(level = KtUnsupportedConstantValue, message = don't use j2)
kotlin/Deprecated(level = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/DeprecationLevel.HIDDEN)), message = don't use j2)
psi: KtAnnotationEntry
]
callableIdIfNonLocal: /j2
@@ -3,7 +3,7 @@ KtFirNamedClassOrObjectSymbol:
kotlin/annotation/Target
]
annotations: [
kotlin/annotation/Target(allowedTargets = KtUnsupportedConstantValue)
kotlin/annotation/Target(allowedTargets = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/annotation/AnnotationTarget.TYPE)))
psi: KtAnnotationEntry
]
classIdIfNonLocal: Anno1
@@ -30,7 +30,7 @@ KtFirNamedClassOrObjectSymbol:
kotlin/annotation/Target
]
annotations: [
kotlin/annotation/Target(allowedTargets = KtUnsupportedConstantValue)
kotlin/annotation/Target(allowedTargets = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/annotation/AnnotationTarget.TYPE)))
psi: KtAnnotationEntry
]
classIdIfNonLocal: Anno2
@@ -57,7 +57,7 @@ KtFirNamedClassOrObjectSymbol:
kotlin/annotation/Target
]
annotations: [
kotlin/annotation/Target(allowedTargets = KtUnsupportedConstantValue)
kotlin/annotation/Target(allowedTargets = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/annotation/AnnotationTarget.TYPE)))
psi: KtAnnotationEntry
]
classIdIfNonLocal: Anno3
@@ -84,7 +84,7 @@ KtFirNamedClassOrObjectSymbol:
kotlin/annotation/Target
]
annotations: [
kotlin/annotation/Target(allowedTargets = KtUnsupportedConstantValue)
kotlin/annotation/Target(allowedTargets = KtEnumEntryValue(KtFirEnumEntrySymbol(kotlin/annotation/AnnotationTarget.TYPE)))
psi: KtAnnotationEntry
]
classIdIfNonLocal: Anno4