From fa517180b7d53625f81f8c94ad99c2346aaca220 Mon Sep 17 00:00:00 2001 From: Justin Paupore Date: Mon, 13 Feb 2023 20:16:15 -0800 Subject: [PATCH] [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. --- .../KtFe10CompileTimeConstantProvider.kt | 15 ++++---------- ...ileTimeConstantEvaluatorTestGenerated.java | 12 +++++++++++ .../FirCompileTimeConstantEvaluator.kt | 20 ++++++++++++++++++- ...ileTimeConstantEvaluatorTestGenerated.java | 12 +++++++++++ ...ileTimeConstantEvaluatorTestGenerated.java | 12 +++++++++++ ...ileTimeConstantEvaluatorTestGenerated.java | 12 +++++++++++ .../evaluate/javaFinalField.txt | 4 ++-- ...nit_DivByOtherProperty_val.descriptors.txt | 9 --------- .../evaluate/string_templateConst.kt | 4 ++++ .../evaluate/string_templateConst.txt | 9 +++++++++ .../evaluate/string_templateNonConst.kt | 5 +++++ .../evaluate/string_templateNonConst.txt | 9 +++++++++ 12 files changed, 100 insertions(+), 23 deletions(-) delete mode 100644 analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/propertyInit_DivByOtherProperty_val.descriptors.txt create mode 100644 analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.kt create mode 100644 analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateConst.txt create mode 100644 analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.kt create mode 100644 analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_templateNonConst.txt 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 f733cda2a08..c58e4ad90af 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 @@ 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() } } 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 5a9918d3b2f..0c4c02180ef 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 @@ -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 { 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 7cc200c1f0f..8b88824aa61 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.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? { + 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 4d1ab4800b4..bbf9259be5e 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 @@ -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 { 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 f12f7ad43fb..2b40bc8151b 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 @@ -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 { 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 714d54ab101..00142ea12e6 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 @@ -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 { diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/javaFinalField.txt index 700c8b47529..d9b12f56ba2 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 9cd860bcbd0..00000000000 --- 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 00000000000..01f3700883b --- /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 = "The answer to ${QUESTION} is ${ANSWER}." 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 00000000000..b0a74ff2692 --- /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 00000000000..16923987ede --- /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 = "The answer to ${QUESTION} is ${ANSWER}. The question is: ${QUESTION_MEANING}" 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 00000000000..a11063a7bde --- /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