[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.
diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CompileTimeConstantProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CompileTimeConstantProvider.kt
index f733cda..c58e4ad 100644
--- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CompileTimeConstantProvider.kt
+++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10CompileTimeConstantProvider.kt
@@ -31,18 +31,11 @@
): 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()
}
}
diff --git a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/compileTimeConstantProvider/Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/compileTimeConstantProvider/Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
index 5a9918d..0c4c021 100644
--- a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/compileTimeConstantProvider/Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
+++ b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/compileTimeConstantProvider/Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
@@ -233,6 +233,18 @@
}
@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 {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt");
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/evaluate/FirCompileTimeConstantEvaluator.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/evaluate/FirCompileTimeConstantEvaluator.kt
index 7cc200c..8b88824 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/evaluate/FirCompileTimeConstantEvaluator.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/evaluate/FirCompileTimeConstantEvaluator.kt
@@ -15,6 +15,7 @@
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 @@
is FirFunctionCall -> {
evaluateFunctionCall(fir, mode)
}
+ is FirStringConcatenationCall -> {
+ evaluateStringConcatenationCall(fir, mode)
+ }
is FirNamedReference -> {
fir.toResolvedPropertySymbol()?.toConstExpression(mode)
}
@@ -93,7 +97,7 @@
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 @@
)
}
+ 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,
diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
index 4d1ab48..bbf9259 100644
--- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
+++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
@@ -233,6 +233,18 @@
}
@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 {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt");
diff --git a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
index f12f7ad..2b40bc8 100644
--- a/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
+++ b/analysis/analysis-api-fir/tests-gen/org/jetbrains/kotlin/analysis/api/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
@@ -233,6 +233,18 @@
}
@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 {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt");
diff --git a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
index 714d54a..00142ea 100644
--- a/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
+++ b/analysis/analysis-api-standalone/tests-gen/org/jetbrains/kotlin/analysis/api/standalone/fir/test/cases/generated/cases/components/compileTimeConstantProvider/FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGenerated.java
@@ -233,6 +233,18 @@
}
@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 {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt");
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt
index 700c8b4..d9b12f5 100644
--- a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt
@@ -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
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/propertyInit_DivByOtherProperty_val.descriptors.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/propertyInit_DivByOtherProperty_val.descriptors.txt
deleted file mode 100644
index 9cd860b..0000000
--- a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/propertyInit_DivByOtherProperty_val.descriptors.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-expression: 42 / d
-
-CONSTANT_EXPRESSION_EVALUATION
-constant: 42
-constantValueKind: Int
-
-CONSTANT_LIKE_EXPRESSION_EVALUATION
-constantLike: 42
-constantLikeValueKind: Int
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt
new file mode 100644
index 0000000..01f3700
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt
@@ -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>
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.txt
new file mode 100644
index 0000000..b0a74ff
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.txt
@@ -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
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt
new file mode 100644
index 0000000..1692398
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt
@@ -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>
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.txt
new file mode 100644
index 0000000..a11063a
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.txt
@@ -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