Allow to use emptyArray in annotation as argument

#KT-14236 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2017-02-06 20:17:03 +03:00
parent 9f0403f72c
commit d7093db5c5
10 changed files with 62 additions and 20 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2017 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -56,7 +56,8 @@ public class CompileTimeConstantUtils {
"kotlin.charArrayOf", "kotlin.charArrayOf",
"kotlin.shortArrayOf", "kotlin.shortArrayOf",
"kotlin.byteArrayOf", "kotlin.byteArrayOf",
"kotlin.booleanArrayOf" "kotlin.booleanArrayOf",
"kotlin.emptyArray"
); );
public static void checkConstructorParametersType(@NotNull List<KtParameter> parameters, @NotNull BindingTrace trace) { public static void checkConstructorParametersType(@NotNull List<KtParameter> parameters, @NotNull BindingTrace trace) {
@@ -110,7 +111,7 @@ public class CompileTimeConstantUtils {
return false; return false;
} }
public static boolean isArrayMethodCall(@NotNull ResolvedCall<?> resolvedCall) { public static boolean isArrayFunctionCall(@NotNull ResolvedCall<?> resolvedCall) {
return ARRAY_CALL_NAMES.contains(DescriptorUtils.getFqName(resolvedCall.getCandidateDescriptor()).asString()); return ARRAY_CALL_NAMES.contains(DescriptorUtils.getFqName(resolvedCall.getCandidateDescriptor()).asString());
} }
@@ -166,23 +166,24 @@ class ConstantExpressionEvaluator(
trace: BindingTrace trace: BindingTrace
): Pair<List<KtExpression>, KotlinType?>? { ): Pair<List<KtExpression>, KotlinType?>? {
val resolvedCall = expression.getResolvedCall(trace.bindingContext) val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall == null || !CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) { if (resolvedCall == null || !CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)) {
return null return null
} }
val argumentEntry = resolvedCall.valueArguments.entries.single() val returnType = resolvedCall.resultingDescriptor.returnType ?: return null
val componentType = builtIns.getArrayElementType(returnType)
val elementType = argumentEntry.key.varargElementType ?: return null
val result = arrayListOf<KtExpression>() val result = arrayListOf<KtExpression>()
for (valueArgument in argumentEntry.value.arguments) { for ((_, resolvedValueArgument) in resolvedCall.valueArguments) {
val valueArgumentExpression = valueArgument.getArgumentExpression() for (valueArgument in resolvedValueArgument.arguments) {
if (valueArgumentExpression != null) { val valueArgumentExpression = valueArgument.getArgumentExpression()
result.add(valueArgumentExpression) if (valueArgumentExpression != null) {
result.add(valueArgumentExpression)
}
} }
} }
return Pair<List<KtExpression>, KotlinType>(result, elementType) return Pair<List<KtExpression>, KotlinType>(result, componentType)
} }
private fun hasSpread(argument: ResolvedValueArgument): Boolean { private fun hasSpread(argument: ResolvedValueArgument): Boolean {
@@ -646,13 +647,14 @@ private class ConstantExpressionEvaluatorVisitor(
val resultingDescriptor = call.resultingDescriptor val resultingDescriptor = call.resultingDescriptor
// arrayOf() // arrayOf() or emptyArray()
if (CompileTimeConstantUtils.isArrayMethodCall(call)) { if (CompileTimeConstantUtils.isArrayFunctionCall(call)) {
val varargType = resultingDescriptor.valueParameters.first().varargElementType!! val returnType = resultingDescriptor.returnType ?: return null
val componentType = constantExpressionEvaluator.builtIns.getArrayElementType(returnType)
val arguments = call.valueArguments.values.flatMap { resolveArguments(it.arguments, varargType) } val arguments = call.valueArguments.values.flatMap { resolveArguments(it.arguments, componentType) }
return factory.createArrayValue(arguments.map { it.toConstantValue(varargType) }, resultingDescriptor.returnType!!). return factory.createArrayValue(arguments.map { it.toConstantValue(componentType) }, resultingDescriptor.returnType!!).
wrap( wrap(
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant }, usesVariableAsConstant = arguments.any { it.usesVariableAsConstant },
usesNonConstValAsConstant = arguments.any { it.usesNonConstValAsConstant } usesNonConstValAsConstant = arguments.any { it.usesNonConstValAsConstant }
@@ -0,0 +1,8 @@
annotation class Anno(val x: Array<String> = emptyArray())
@Anno fun test1() = 1
@Anno(arrayOf("K")) fun test2() = 2
fun box(): String {
return if (test1() + test2() == 3) "OK" else "Fail"
}
@@ -0,0 +1,12 @@
@java.lang.annotation.Retention
@kotlin.Metadata
public annotation class Anno {
public abstract method x(): java.lang.String[]
}
@kotlin.Metadata
public final class AnnotationWithEmptyArrayKt {
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static @Anno method test1(): int
public final static @Anno method test2(): int
}
@@ -19,6 +19,7 @@ val i2 = foo()
fun foo(): Int = 1 fun foo(): Int = 1
@AnnSA(emptyArray())
class MyClass { class MyClass {
val i = 1 val i = 1
} }
@@ -37,7 +37,7 @@ public final annotation class AnnSA : kotlin.Annotation {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
public final class MyClass { @AnnSA(sa = {}) public final class MyClass {
public constructor MyClass() public constructor MyClass()
public final val i: kotlin.Int = 1 public final val i: kotlin.Int = 1
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@@ -5845,6 +5845,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("annotationWithEmptyArray.kt")
public void testAnnotationWithEmptyArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/annotationWithEmptyArray.kt");
doTest(fileName);
}
@TestMetadata("checkIfConstructorIsSynthetic.kt") @TestMetadata("checkIfConstructorIsSynthetic.kt")
public void testCheckIfConstructorIsSynthetic() throws Exception { public void testCheckIfConstructorIsSynthetic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt");
@@ -5845,6 +5845,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("annotationWithEmptyArray.kt")
public void testAnnotationWithEmptyArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/annotationWithEmptyArray.kt");
doTest(fileName);
}
@TestMetadata("checkIfConstructorIsSynthetic.kt") @TestMetadata("checkIfConstructorIsSynthetic.kt")
public void testCheckIfConstructorIsSynthetic() throws Exception { public void testCheckIfConstructorIsSynthetic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt");
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2017 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -500,7 +500,7 @@ class CodeInliner<TCallElement : KtElement>(
val argumentExpression = argument.getArgumentExpression() ?: return@forEachDescendantOfType val argumentExpression = argument.getArgumentExpression() ?: return@forEachDescendantOfType
val resolvedCall = argumentExpression.getResolvedCall(argumentExpression.analyze(BodyResolveMode.PARTIAL)) ?: return@forEachDescendantOfType val resolvedCall = argumentExpression.getResolvedCall(argumentExpression.analyze(BodyResolveMode.PARTIAL)) ?: return@forEachDescendantOfType
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return@forEachDescendantOfType val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return@forEachDescendantOfType
if (CompileTimeConstantUtils.isArrayMethodCall(resolvedCall)) { if (CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)) {
argumentsToExpand.add(argument to callExpression.valueArguments) argumentsToExpand.add(argument to callExpression.valueArguments)
} }
} }
@@ -6614,6 +6614,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("annotationWithEmptyArray.kt")
public void testAnnotationWithEmptyArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/annotationWithEmptyArray.kt");
doTest(fileName);
}
@TestMetadata("checkIfConstructorIsSynthetic.kt") @TestMetadata("checkIfConstructorIsSynthetic.kt")
public void testCheckIfConstructorIsSynthetic() throws Exception { public void testCheckIfConstructorIsSynthetic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt");