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 80e5f5565b3..a4654103a3a 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
@@ -189,4 +189,40 @@ public class Fe10IdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGe
public void testStringLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/stringLiteral.kt");
}
+
+ @Test
+ @TestMetadata("string_compareTo.kt")
+ public void testString_compareTo() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt");
+ }
+
+ @Test
+ @TestMetadata("string_length.kt")
+ public void testString_length() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusMany.kt")
+ public void testString_plusMany() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusOnce.kt")
+ public void testString_plusOnce() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusTwice.kt")
+ public void testString_plusTwice() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.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 1af62376230..20321cf7a7c 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
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.*
+import org.jetbrains.kotlin.name.CallableId
+import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.constants.evaluate.CompileTimeType
import org.jetbrains.kotlin.resolve.constants.evaluate.evalBinaryOp
@@ -47,7 +49,13 @@ internal object FirCompileTimeConstantEvaluator {
when (fir) {
is FirPropertyAccessExpression -> {
when (val referredVariable = fir.referredVariableSymbol) {
- is FirPropertySymbol -> referredVariable.toConstExpression(mode)
+ is FirPropertySymbol -> {
+ if (referredVariable.callableId.isStringLength) {
+ evaluate(fir.explicitReceiver, mode)?.evaluateStringLength()
+ } else {
+ referredVariable.toConstExpression(mode)
+ }
+ }
is FirFieldSymbol -> referredVariable.toConstExpression(mode)
else -> null
}
@@ -67,6 +75,9 @@ internal object FirCompileTimeConstantEvaluator {
else -> null
}
+ private val CallableId.isStringLength: Boolean
+ get() = classId == StandardClassIds.String && callableName.identifierOrNullIfSpecial == "length"
+
private fun FirPropertySymbol.toConstExpression(
mode: KtConstantEvaluationMode,
): FirConstExpression<*>? {
@@ -172,7 +183,15 @@ internal object FirCompileTimeConstantEvaluator {
// Unary operators
private fun FirConstExpression<*>.evaluate(function: FirSimpleFunction): FirConstExpression<*>? {
if (value == null) return null
- // TODO: there are a couple operations on String, such as .length and .toString
+ (value as? String)?.let { opr ->
+ evalUnaryOp(
+ function.name.asString(),
+ kind.toCompileTimeType(),
+ opr
+ )?.let {
+ return it.toConstantValueKind().toConstExpression(source, it)
+ }
+ }
return kind.convertToNumber(value as? Number)?.let { opr ->
evalUnaryOp(
function.name.asString(),
@@ -184,13 +203,37 @@ internal object FirCompileTimeConstantEvaluator {
}
}
+ private fun FirConstExpression<*>.evaluateStringLength(): FirConstExpression<*>? {
+ return (value as? String)?.length?.let {
+ it.toConstantValueKind().toConstExpression(source, it)
+ }
+ }
+
// Binary operators
private fun FirConstExpression<*>.evaluate(
function: FirSimpleFunction,
other: FirConstExpression<*>
): FirConstExpression<*>? {
if (value == null || other.value == null) return null
- // TODO: there are a couple operations on Strings, such as .compareTo, .equals, or .plus
+ // NB: some utils accept very general types, and due to the way operation map works, we should up-cast rhs type.
+ val rightType = when {
+ function.symbol.callableId.isStringEquals -> CompileTimeType.ANY
+ function.symbol.callableId.isStringPlus -> CompileTimeType.ANY
+ else -> other.kind.toCompileTimeType()
+ }
+ (value as? String)?.let { opr1 ->
+ other.value?.let { opr2 ->
+ evalBinaryOp(
+ function.name.asString(),
+ kind.toCompileTimeType(),
+ opr1,
+ rightType,
+ opr2
+ )?.let {
+ return it.toConstantValueKind().toConstExpression(source, it)
+ }
+ }
+ }
return kind.convertToNumber(value as? Number)?.let { opr1 ->
other.kind.convertToNumber(other.value as? Number)?.let { opr2 ->
evalBinaryOp(
@@ -206,6 +249,12 @@ internal object FirCompileTimeConstantEvaluator {
}
}
+ private val CallableId.isStringEquals: Boolean
+ get() = classId == StandardClassIds.String && callableName.identifierOrNullIfSpecial == "equals"
+
+ private val CallableId.isStringPlus: Boolean
+ get() = classId == StandardClassIds.String && callableName.identifierOrNullIfSpecial == "plus"
+
////// KINDS
private fun FirTypeRef.toConstantValueKind(): ConstantValueKind<*>? =
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 737f357f379..317f264a17a 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
@@ -189,4 +189,40 @@ public class FirIdeDependentAnalysisSourceModuleCompileTimeConstantEvaluatorTest
public void testStringLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/stringLiteral.kt");
}
+
+ @Test
+ @TestMetadata("string_compareTo.kt")
+ public void testString_compareTo() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt");
+ }
+
+ @Test
+ @TestMetadata("string_length.kt")
+ public void testString_length() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusMany.kt")
+ public void testString_plusMany() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusOnce.kt")
+ public void testString_plusOnce() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusTwice.kt")
+ public void testString_plusTwice() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.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 a4a68a45e64..69757c16404 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
@@ -189,4 +189,40 @@ public class FirIdeNormalAnalysisSourceModuleCompileTimeConstantEvaluatorTestGen
public void testStringLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/stringLiteral.kt");
}
+
+ @Test
+ @TestMetadata("string_compareTo.kt")
+ public void testString_compareTo() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt");
+ }
+
+ @Test
+ @TestMetadata("string_length.kt")
+ public void testString_length() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusMany.kt")
+ public void testString_plusMany() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusOnce.kt")
+ public void testString_plusOnce() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusTwice.kt")
+ public void testString_plusTwice() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.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 a9223677cca..a42fd889b66 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
@@ -189,4 +189,40 @@ public class FirStandaloneNormalAnalysisSourceModuleCompileTimeConstantEvaluator
public void testStringLiteral() throws Exception {
runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/stringLiteral.kt");
}
+
+ @Test
+ @TestMetadata("string_compareTo.kt")
+ public void testString_compareTo() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt");
+ }
+
+ @Test
+ @TestMetadata("string_length.kt")
+ public void testString_length() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusMany.kt")
+ public void testString_plusMany() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusOnce.kt")
+ public void testString_plusOnce() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt");
+ }
+
+ @Test
+ @TestMetadata("string_plusTwice.kt")
+ public void testString_plusTwice() throws Exception {
+ runTest("analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.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/string_compareTo.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt
new file mode 100644
index 00000000000..ba18189ce03
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.kt
@@ -0,0 +1,4 @@
+fun box() {
+ if ("a".compareTo("b") > 0)
+ TODO("no way!")
+}
\ No newline at end of file
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.txt
new file mode 100644
index 00000000000..41d696bd404
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_compareTo.txt
@@ -0,0 +1,9 @@
+expression: "a".compareTo("b")
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: -1
+constantValueKind: Int
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: -1
+constantLikeValueKind: Int
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt
new file mode 100644
index 00000000000..05250de39a4
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.kt
@@ -0,0 +1 @@
+const val LENGTH = "Hello, world".length
\ No newline at end of file
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.txt
new file mode 100644
index 00000000000..c959145053a
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_length.txt
@@ -0,0 +1,9 @@
+expression: "Hello, world".length
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: 12
+constantValueKind: Int
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: 12
+constantLikeValueKind: Int
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt
new file mode 100644
index 00000000000..b71303b3333
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.kt
@@ -0,0 +1 @@
+const val PASSWORD = "na" + "na" + "na" + "na" + "na" + "na" + "na" + "na " + "batman"
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.txt
new file mode 100644
index 00000000000..043d3011425
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusMany.txt
@@ -0,0 +1,9 @@
+expression: "na" + "na" + "na" + "na" + "na" + "na" + "na" + "na " + "batman"
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: "nananananananana batman"
+constantValueKind: String
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: "nananananananana batman"
+constantLikeValueKind: String
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt
new file mode 100644
index 00000000000..f34595d0a7f
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.kt
@@ -0,0 +1 @@
+const val PASSWORD = "nananananananana " + "batman"
\ No newline at end of file
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.txt
new file mode 100644
index 00000000000..8b38a366b73
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusOnce.txt
@@ -0,0 +1,9 @@
+expression: "nananananananana " + "batman"
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: "nananananananana batman"
+constantValueKind: String
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: "nananananananana batman"
+constantLikeValueKind: String
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt
new file mode 100644
index 00000000000..37a34bab431
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.kt
@@ -0,0 +1 @@
+const val PASSWORD = "nananana" + "nananana " + "batman"
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.txt
new file mode 100644
index 00000000000..bdaca00e807
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_plusTwice.txt
@@ -0,0 +1,9 @@
+expression: "nananana" + "nananana " + "batman"
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: "nananananananana batman"
+constantValueKind: String
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: "nananananananana batman"
+constantLikeValueKind: String
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt
new file mode 100644
index 00000000000..47f99de3e1c
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.kt
@@ -0,0 +1 @@
+const val TAG = "Analysis API".toString()
\ No newline at end of file
diff --git a/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.txt b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.txt
new file mode 100644
index 00000000000..d682c463b06
--- /dev/null
+++ b/analysis/analysis-api/testData/components/compileTimeConstantProvider/evaluate/string_toString.txt
@@ -0,0 +1,9 @@
+expression: "Analysis API".toString()
+
+CONSTANT_EXPRESSION_EVALUATION
+constant: "Analysis API"
+constantValueKind: String
+
+CONSTANT_LIKE_EXPRESSION_EVALUATION
+constantLike: "Analysis API"
+constantLikeValueKind: String