AA FIR: constant evaluation for String

This commit is contained in:
Jinseong Jeon
2022-08-29 22:13:32 -07:00
committed by Ilya Kirillov
parent 0d1b748cd5
commit 836b6dae9f
17 changed files with 259 additions and 3 deletions
@@ -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");
}
}
@@ -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<*>? =
@@ -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");
}
}
@@ -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");
}
}
@@ -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");
}
}
@@ -0,0 +1,4 @@
fun box() {
if (<expr>"a".compareTo("b")</expr> > 0)
TODO("no way!")
}
@@ -0,0 +1,9 @@
expression: "a".compareTo("b")
CONSTANT_EXPRESSION_EVALUATION
constant: -1
constantValueKind: Int
CONSTANT_LIKE_EXPRESSION_EVALUATION
constantLike: -1
constantLikeValueKind: Int
@@ -0,0 +1 @@
const val LENGTH = <expr>"Hello, world".length</expr>
@@ -0,0 +1,9 @@
expression: "Hello, world".length
CONSTANT_EXPRESSION_EVALUATION
constant: 12
constantValueKind: Int
CONSTANT_LIKE_EXPRESSION_EVALUATION
constantLike: 12
constantLikeValueKind: Int
@@ -0,0 +1 @@
const val PASSWORD = <expr>"na" + "na" + "na" + "na" + "na" + "na" + "na" + "na " + "batman"</expr>
@@ -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
@@ -0,0 +1 @@
const val PASSWORD = <expr>"nananananananana " + "batman"</expr>
@@ -0,0 +1,9 @@
expression: "nananananananana " + "batman"
CONSTANT_EXPRESSION_EVALUATION
constant: "nananananananana batman"
constantValueKind: String
CONSTANT_LIKE_EXPRESSION_EVALUATION
constantLike: "nananananananana batman"
constantLikeValueKind: String
@@ -0,0 +1 @@
const val PASSWORD = <expr>"nananana" + "nananana " + "batman"</expr>
@@ -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
@@ -0,0 +1 @@
const val TAG = <expr>"Analysis API".toString()</expr>
@@ -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