Don't allow use standalone constants in complexExpressions, fix for KT-11043: Inconsisten result for class literal and string concatenation
#KT-11043 Fixed
This commit is contained in:
+24
@@ -250,6 +250,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
private val stringExpressionEvaluator = object : KtVisitor<TypedCompileTimeConstant<String>, Nothing?>() {
|
private val stringExpressionEvaluator = object : KtVisitor<TypedCompileTimeConstant<String>, Nothing?>() {
|
||||||
private fun createStringConstant(compileTimeConstant: CompileTimeConstant<*>): TypedCompileTimeConstant<String>? {
|
private fun createStringConstant(compileTimeConstant: CompileTimeConstant<*>): TypedCompileTimeConstant<String>? {
|
||||||
val constantValue = compileTimeConstant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)
|
val constantValue = compileTimeConstant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)
|
||||||
|
if (constantValue.isStandaloneOnlyConstant()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return when (constantValue) {
|
return when (constantValue) {
|
||||||
is ErrorValue, is EnumValue -> return null
|
is ErrorValue, is EnumValue -> return null
|
||||||
is NullValue -> factory.createStringValue("null")
|
is NullValue -> factory.createStringValue("null")
|
||||||
@@ -341,6 +344,10 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isStandaloneOnlyConstant(expression: KtExpression): Boolean {
|
||||||
|
return ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)?.isStandaloneOnlyConstant() ?: return false
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||||
val compileTimeConstant = evaluate(expression.left, expectedType)
|
val compileTimeConstant = evaluate(expression.left, expectedType)
|
||||||
if (compileTimeConstant != null) {
|
if (compileTimeConstant != null) {
|
||||||
@@ -404,6 +411,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
val resultingDescriptorName = resolvedCall.resultingDescriptor.name
|
val resultingDescriptorName = resolvedCall.resultingDescriptor.name
|
||||||
|
|
||||||
val argumentForReceiver = createOperationArgumentForReceiver(resolvedCall, receiverExpression) ?: return null
|
val argumentForReceiver = createOperationArgumentForReceiver(resolvedCall, receiverExpression) ?: return null
|
||||||
|
if (isStandaloneOnlyConstant(argumentForReceiver.expression)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val argumentsEntrySet = resolvedCall.valueArguments.entries
|
val argumentsEntrySet = resolvedCall.valueArguments.entries
|
||||||
if (argumentsEntrySet.isEmpty()) {
|
if (argumentsEntrySet.isEmpty()) {
|
||||||
@@ -426,6 +436,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
else if (argumentsEntrySet.size == 1) {
|
else if (argumentsEntrySet.size == 1) {
|
||||||
val (parameter, argument) = argumentsEntrySet.first()
|
val (parameter, argument) = argumentsEntrySet.first()
|
||||||
val argumentForParameter = createOperationArgumentForFirstParameter(argument, parameter) ?: return null
|
val argumentForParameter = createOperationArgumentForFirstParameter(argument, parameter) ?: return null
|
||||||
|
if (isStandaloneOnlyConstant(argumentForParameter.expression)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
|
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
|
||||||
val parentExpression: KtExpression = PsiTreeUtil.getParentOfType(receiverExpression, KtExpression::class.java)!!
|
val parentExpression: KtExpression = PsiTreeUtil.getParentOfType(receiverExpression, KtExpression::class.java)!!
|
||||||
@@ -886,3 +899,14 @@ internal fun <A> unaryOperation(
|
|||||||
|
|
||||||
internal data class BinaryOperationKey<A, B>(val f: CompileTimeType<out A>, val s: CompileTimeType<out B>, val functionName: String)
|
internal data class BinaryOperationKey<A, B>(val f: CompileTimeType<out A>, val s: CompileTimeType<out B>, val functionName: String)
|
||||||
internal data class UnaryOperationKey<A>(val f: CompileTimeType<out A>, val functionName: String)
|
internal data class UnaryOperationKey<A>(val f: CompileTimeType<out A>, val functionName: String)
|
||||||
|
|
||||||
|
fun ConstantValue<*>.isStandaloneOnlyConstant(): Boolean {
|
||||||
|
return this is KClassValue || this is EnumValue || this is AnnotationValue || this is ArrayValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CompileTimeConstant<*>.isStandaloneOnlyConstant(): Boolean {
|
||||||
|
return when(this) {
|
||||||
|
is TypedCompileTimeConstant -> this.constantValue.isStandaloneOnlyConstant()
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
annotation class AnnE(val i: String)
|
||||||
|
|
||||||
|
enum class MyEnum {
|
||||||
|
A
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnE(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>"1" + MyEnum.A<!>)
|
||||||
|
class Test
|
||||||
|
|
||||||
|
@AnnE(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>"1" + MyEnum::class<!>)
|
||||||
|
class Test2
|
||||||
|
|
||||||
|
@AnnE(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>"1" + AnnE("23")<!>)
|
||||||
|
class Test3
|
||||||
|
|
||||||
|
@AnnE(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>"1" + arrayOf("23", "34")<!>)
|
||||||
|
class Test4
|
||||||
|
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final annotation class AnnE : kotlin.Annotation {
|
||||||
|
public constructor AnnE(/*0*/ i: kotlin.String)
|
||||||
|
public final val i: kotlin.String
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final enum class MyEnum : kotlin.Enum<MyEnum> {
|
||||||
|
enum entry A
|
||||||
|
|
||||||
|
private constructor MyEnum()
|
||||||
|
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||||
|
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||||
|
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||||
|
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
|
||||||
|
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
// Static members
|
||||||
|
@kotlin.Deprecated(level = DeprecationLevel.ERROR, message = "Use 'values()' function instead", replaceWith = kotlin.ReplaceWith(expression = "this.values()", imports = {})) public final /*synthesized*/ val values: kotlin.Array<MyEnum>
|
||||||
|
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum
|
||||||
|
public final /*synthesized*/ fun values(): kotlin.Array<MyEnum>
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnE() public final class Test {
|
||||||
|
public constructor Test()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnE() public final class Test2 {
|
||||||
|
public constructor Test2()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnE() public final class Test3 {
|
||||||
|
public constructor Test3()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnE() public final class Test4 {
|
||||||
|
public constructor Test4()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -1163,6 +1163,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("standaloneInExpression.kt")
|
||||||
|
public void testStandaloneInExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/standaloneInExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("strings.kt")
|
@TestMetadata("strings.kt")
|
||||||
public void testStrings() throws Exception {
|
public void testStrings() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/strings.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/strings.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user