[AA-FIR] Add support for constant evaluation of string templates.
Support FirStringConcatenationCall in FirCompileTimeConstantEvaluator.
This allows string templates ("foo${bar}") to be evaluated as constants,
assuming the interpolated expressions are themselves constant.
In addition, fixes some handling bugs with KtConstantEvaluationMode,
where some expressions that are not valid in a `const val` declaration
were being supported for `CONSTANT_EXPRESSION_EVALUATION`, including
non-static final Java fields in FIR, and composite expressions of
non-const properties in FE1.0.
This commit is contained in:
committed by
Ilya Kirillov
parent
0193c83b05
commit
fa517180b7
+4
-11
@@ -31,18 +31,11 @@ internal class KtFe10CompileTimeConstantProvider(
|
||||
): KtConstantValue? {
|
||||
val bindingContext = analysisContext.analyze(expression)
|
||||
|
||||
if (mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION) {
|
||||
// TODO: how to _not_ evaluate composite expression with a non-const property?
|
||||
// TODO: how to _not_ evaluate expressions with a compilation error, e.g., uninitialized property access
|
||||
when (expression) {
|
||||
is KtNameReferenceExpression -> {
|
||||
val reference = bindingContext[BindingContext.REFERENCE_TARGET, expression]
|
||||
if (reference is PropertyDescriptor && !reference.isConst) return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val constant = ConstantExpressionEvaluator.getPossiblyErrorConstant(expression, bindingContext)
|
||||
if (mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION) {
|
||||
// TODO: how to _not_ evaluate expressions with a compilation error, e.g., uninitialized property access
|
||||
if (constant?.usesNonConstValAsConstant == true) return null
|
||||
}
|
||||
return constant?.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)?.toKtConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -232,6 +232,18 @@ public class Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGe
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateConst.kt")
|
||||
public void testString_templateConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateNonConst.kt")
|
||||
public void testString_templateNonConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_toString.kt")
|
||||
public void testString_toString() throws Exception {
|
||||
|
||||
+19
-1
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isConst
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
@@ -66,6 +67,9 @@ internal object FirCompileTimeConstantEvaluator {
|
||||
is FirFunctionCall -> {
|
||||
evaluateFunctionCall(fir, mode)
|
||||
}
|
||||
is FirStringConcatenationCall -> {
|
||||
evaluateStringConcatenationCall(fir, mode)
|
||||
}
|
||||
is FirNamedReference -> {
|
||||
fir.toResolvedPropertySymbol()?.toConstExpression(mode)
|
||||
}
|
||||
@@ -93,7 +97,7 @@ internal object FirCompileTimeConstantEvaluator {
|
||||
mode: KtConstantEvaluationMode,
|
||||
): FirConstExpression<*>? {
|
||||
return when {
|
||||
mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION && !isFinal -> null
|
||||
mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION && !(isStatic && isFinal) -> null
|
||||
isVal && hasInitializer -> {
|
||||
evaluate(fir.initializer, mode)
|
||||
}
|
||||
@@ -152,6 +156,20 @@ internal object FirCompileTimeConstantEvaluator {
|
||||
)
|
||||
}
|
||||
|
||||
private fun evaluateStringConcatenationCall(
|
||||
stringConcatenationCall: FirStringConcatenationCall,
|
||||
mode: KtConstantEvaluationMode,
|
||||
): FirConstExpression<String>? {
|
||||
val concatenated = buildString {
|
||||
for (arg in stringConcatenationCall.arguments) {
|
||||
val evaluated = evaluate(arg, mode) ?: return null
|
||||
append(evaluated.value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return ConstantValueKind.String.toConstExpression(stringConcatenationCall.source, concatenated)
|
||||
}
|
||||
|
||||
private fun evaluateFunctionCall(
|
||||
functionCall: FirFunctionCall,
|
||||
mode: KtConstantEvaluationMode,
|
||||
|
||||
+12
@@ -232,6 +232,18 @@ public class FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTest
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateConst.kt")
|
||||
public void testString_templateConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateNonConst.kt")
|
||||
public void testString_templateNonConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_toString.kt")
|
||||
public void testString_toString() throws Exception {
|
||||
|
||||
+12
@@ -232,6 +232,18 @@ public class FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGen
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateConst.kt")
|
||||
public void testString_templateConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateNonConst.kt")
|
||||
public void testString_templateNonConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_toString.kt")
|
||||
public void testString_toString() throws Exception {
|
||||
|
||||
+12
@@ -232,6 +232,18 @@ public class FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluator
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateConst.kt")
|
||||
public void testString_templateConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_templateNonConst.kt")
|
||||
public void testString_templateNonConst() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("string_toString.kt")
|
||||
public void testString_toString() throws Exception {
|
||||
|
||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
||||
expression: Build.VERSION_CODES().CUPCAKE
|
||||
|
||||
CONSTANT_EXPRESSION_EVALUATION
|
||||
constant: 3
|
||||
constantValueKind: Int
|
||||
constant: NOT_EVALUATED
|
||||
constantValueKind: NOT_EVALUATED
|
||||
|
||||
CONSTANT_LIKE_EXPRESSION_EVALUATION
|
||||
constantLike: 3
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
expression: 42 / d
|
||||
|
||||
CONSTANT_EXPRESSION_EVALUATION
|
||||
constant: 42
|
||||
constantValueKind: Int
|
||||
|
||||
CONSTANT_LIKE_EXPRESSION_EVALUATION
|
||||
constantLike: 42
|
||||
constantLikeValueKind: Int
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
const val QUESTION = "the ultimate question of life, the universe, and everything"
|
||||
const val ANSWER = 42
|
||||
|
||||
val MESSAGE = <expr>"The answer to ${QUESTION} is ${ANSWER}."</expr>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
expression: "The answer to ${QUESTION} is ${ANSWER}."
|
||||
|
||||
CONSTANT_EXPRESSION_EVALUATION
|
||||
constant: "The answer to the ultimate question of life, the universe, and everything is 42."
|
||||
constantValueKind: String
|
||||
|
||||
CONSTANT_LIKE_EXPRESSION_EVALUATION
|
||||
constantLike: "The answer to the ultimate question of life, the universe, and everything is 42."
|
||||
constantLikeValueKind: String
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
val QUESTION = "the ultimate question of life, the universe, and everything"
|
||||
val ANSWER = 42
|
||||
val QUESTION_MEANING = null
|
||||
|
||||
val MESSAGE = <expr>"The answer to ${QUESTION} is ${ANSWER}. The question is: ${QUESTION_MEANING}"</expr>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
expression: "The answer to ${QUESTION} is ${ANSWER}. The question is: ${QUESTION_MEANING}"
|
||||
|
||||
CONSTANT_EXPRESSION_EVALUATION
|
||||
constant: NOT_EVALUATED
|
||||
constantValueKind: NOT_EVALUATED
|
||||
|
||||
CONSTANT_LIKE_EXPRESSION_EVALUATION
|
||||
constantLike: "The answer to the ultimate question of life, the universe, and everything is 42. The question is: null"
|
||||
constantLikeValueKind: String
|
||||
Reference in New Issue
Block a user