LL: cache reverse converted constant expression
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompileTimeConstantProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompileTimeConstantProvider.kt
index a717b4b..9205fa4 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompileTimeConstantProvider.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompileTimeConstantProvider.kt
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
-import org.jetbrains.kotlin.analysis.api.base.KtConstantValueFactory
import org.jetbrains.kotlin.analysis.api.components.KtCompileTimeConstantProvider
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
@@ -16,16 +15,11 @@
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.FirElement
-import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenBranch
import org.jetbrains.kotlin.fir.references.FirNamedReference
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.psi.KtPrefixExpression
-import org.jetbrains.kotlin.types.ConstantValueKind
internal class KtFirCompileTimeConstantProvider(
override val analysisSession: KtFirAnalysisSession,
@@ -45,13 +39,6 @@
mode: KtConstantEvaluationMode,
): KtConstantValue? = withValidityAssertion {
when (fir) {
- is FirConstExpression<*> -> {
- val v = FirCompileTimeConstantEvaluator.evaluateAsKtConstantValue(fir, mode)
- if (v != null && fir.isConverted(sourcePsi)) {
- val value = fir.kind.unaryMinus(v.value as? Number) ?: return null
- KtConstantValueFactory.createConstantValue(value, v.sourcePsi)
- } else v
- }
is FirPropertyAccessExpression,
is FirExpression,
is FirNamedReference -> {
@@ -74,23 +61,4 @@
}
}
- private fun FirConstExpression<*>.isConverted(sourcePsi: KtExpression): Boolean {
- val firSourcePsi = this.source?.psi ?: return false
- return firSourcePsi is KtPrefixExpression && firSourcePsi.operationToken == KtTokens.MINUS && firSourcePsi != sourcePsi
- }
-
- private fun ConstantValueKind<*>.unaryMinus(value: Number?): Any? {
- if (value == null) {
- return null
- }
- return when (this) {
- ConstantValueKind.Byte -> value.toByte().unaryMinus()
- ConstantValueKind.Double -> value.toDouble().unaryMinus()
- ConstantValueKind.Float -> value.toFloat().unaryMinus()
- ConstantValueKind.Int -> value.toInt().unaryMinus()
- ConstantValueKind.Long -> value.toLong().unaryMinus()
- ConstantValueKind.Short -> value.toShort().unaryMinus()
- else -> null
- }
- }
}
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
index 5ddf014..3152de5 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
@@ -5,15 +5,15 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
-import org.jetbrains.kotlin.KtFakeSourceElementKind
-import org.jetbrains.kotlin.KtRealPsiSourceElement
-import org.jetbrains.kotlin.KtSourceElement
+import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isErrorElement
import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
+import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -21,11 +21,10 @@
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
import org.jetbrains.kotlin.fir.visitors.FirVisitor
-import org.jetbrains.kotlin.psi
-import org.jetbrains.kotlin.psi.KtBinaryExpression
-import org.jetbrains.kotlin.psi.KtElement
-import org.jetbrains.kotlin.psi.KtTypeReference
-import org.jetbrains.kotlin.psi.KtUnaryExpression
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
+import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.util.OperatorNameConventions
internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement, FirElement>>() {
@@ -71,6 +70,15 @@
visitElement(variableAssignment, data)
}
+ override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: MutableMap<KtElement, FirElement>) {
+ cacheElement(constExpression, data)
+ // KtPrefixExpression(-, KtConstExpression(n)) is represented as FirConstExpression(-n) with converted constant value.
+ // If one queries FIR for KtConstExpression, we still return FirConstExpression(-n) even though its source is KtPrefixExpression.
+ // Here, we cache FirConstExpression(n) for KtConstExpression(n) to make everything natural and intuitive!
+ if (constExpression.isConverted) {
+ constExpression.kind.reverseConverted(constExpression)?.let { cacheElement(it, data) }
+ }
+ }
//@formatter:off
override fun visitReference(reference: FirReference, data: MutableMap<KtElement, FirElement>) {}
@@ -145,9 +153,42 @@
return name == OperatorNameConventions.SET || name in OperatorNameConventions.ASSIGNMENT_OPERATIONS
}
+ private val FirConstExpression<*>.isConverted: Boolean
+ get() {
+ val firSourcePsi = this.source?.psi ?: return false
+ return firSourcePsi is KtPrefixExpression && firSourcePsi.operationToken == KtTokens.MINUS
+ }
+
+ private val FirConstExpression<*>.ktConstantExpression: KtConstantExpression?
+ get() {
+ val firSourcePsi = this.source?.psi
+ return firSourcePsi?.findDescendantOfType()
+ }
+
+ private fun <T> ConstantValueKind<T>.reverseConverted(original: FirConstExpression<T>): FirConstExpression<T>? {
+ val value = original.value as? Number ?: return null
+ val convertedValue = when (this) {
+ ConstantValueKind.Byte -> value.toByte().unaryMinus()
+ ConstantValueKind.Double -> value.toDouble().unaryMinus()
+ ConstantValueKind.Float -> value.toFloat().unaryMinus()
+ ConstantValueKind.Int -> value.toInt().unaryMinus()
+ ConstantValueKind.Long -> value.toLong().unaryMinus()
+ ConstantValueKind.Short -> value.toShort().unaryMinus()
+ else -> null
+ } ?: return null
+ @Suppress("UNCHECKED_CAST")
+ return buildConstExpression(
+ original.ktConstantExpression?.toKtPsiSourceElement(),
+ this,
+ convertedValue as T
+ ).also {
+ it.replaceTypeRef(original.typeRef)
+ }
+ }
+
companion object {
@OptIn(ExperimentalStdlibApi::class)
fun recordElementsFrom(firElement: FirElement, recorder: FirElementsRecorder): Map<KtElement, FirElement> =
buildMap { firElement.accept(recorder, this) }
}
-}
\ No newline at end of file
+}
diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/expressions/intLiteral_minusOne_justOne.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/expressions/intLiteral_minusOne_justOne.txt
index 6f9a566..24ef1c6 100644
--- a/analysis/low-level-api-fir/testdata/getOrBuildFir/expressions/intLiteral_minusOne_justOne.txt
+++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/expressions/intLiteral_minusOne_justOne.txt
@@ -3,4 +3,4 @@
FIR source kind: KtRealSourceElementKind
FIR element rendered:
-Int(-1)
+Int(1)