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:
Michael Bogdanov
2016-02-16 15:48:58 +03:00
parent af3437626b
commit 7c2920febe
4 changed files with 103 additions and 0 deletions
@@ -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
}
}
@@ -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
@@ -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");