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 fun createStringConstant(compileTimeConstant: CompileTimeConstant<*>): TypedCompileTimeConstant<String>? {
|
||||
val constantValue = compileTimeConstant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)
|
||||
if (constantValue.isStandaloneOnlyConstant()) {
|
||||
return null;
|
||||
}
|
||||
return when (constantValue) {
|
||||
is ErrorValue, is EnumValue -> return null
|
||||
is NullValue -> factory.createStringValue("null")
|
||||
@@ -341,6 +344,10 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
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<*>? {
|
||||
val compileTimeConstant = evaluate(expression.left, expectedType)
|
||||
if (compileTimeConstant != null) {
|
||||
@@ -404,6 +411,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
val resultingDescriptorName = resolvedCall.resultingDescriptor.name
|
||||
|
||||
val argumentForReceiver = createOperationArgumentForReceiver(resolvedCall, receiverExpression) ?: return null
|
||||
if (isStandaloneOnlyConstant(argumentForReceiver.expression)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val argumentsEntrySet = resolvedCall.valueArguments.entries
|
||||
if (argumentsEntrySet.isEmpty()) {
|
||||
@@ -426,6 +436,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
else if (argumentsEntrySet.size == 1) {
|
||||
val (parameter, argument) = argumentsEntrySet.first()
|
||||
val argumentForParameter = createOperationArgumentForFirstParameter(argument, parameter) ?: return null
|
||||
if (isStandaloneOnlyConstant(argumentForParameter.expression)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
|
||||
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 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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testStrings() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/strings.kt");
|
||||
|
||||
Reference in New Issue
Block a user