Annotation parameter must be constant on vararg and array arguments

This commit is contained in:
Natalia Ukhorskaya
2014-01-22 13:10:18 +04:00
parent 97da2def08
commit fd3f852a93
5 changed files with 162 additions and 24 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -34,7 +35,6 @@ import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstructor;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.ErrorUtils;
@@ -43,11 +43,13 @@ import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import javax.inject.Inject;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.jetbrains.jet.lang.resolve.BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT;
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.jet.lang.types.TypeUtils.getPrimitiveNumberType;
public class AnnotationResolver {
@@ -215,9 +217,12 @@ public class AnnotationResolver {
ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
JetType varargElementType = parameterDescriptor.getVarargElementType();
List<CompileTimeConstant<?>> constants = resolveValueArguments(descriptorToArgument.getValue(), parameterDescriptor.getType(), trace);
boolean argumentsAsVararg = varargElementType != null && !hasSpread(descriptorToArgument.getValue());
List<CompileTimeConstant<?>> constants = resolveValueArguments(descriptorToArgument.getValue(),
argumentsAsVararg ? varargElementType : parameterDescriptor.getType(),
trace);
if (varargElementType != null && !hasSpread(descriptorToArgument.getValue())) {
if (argumentsAsVararg) {
JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
if (arrayType == null) {
arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
@@ -232,6 +237,71 @@ public class AnnotationResolver {
}
}
private static void checkCompileTimeConstant(
@NotNull JetExpression argumentExpression,
@NotNull JetType expectedType,
@NotNull BindingTrace trace
) {
JetType expressionType = trace.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
if (expressionType == null || !expressionType.equals(expectedType)) {
// TYPE_MISMATCH should be reported otherwise
return;
}
// array(1, <!>null<!>, 3) - error should be reported on inner expression
if (argumentExpression instanceof JetCallExpression) {
Pair<List<JetExpression>, JetType> arrayArgument = getArgumentExpressionsForArrayCall((JetCallExpression) argumentExpression, trace);
if (arrayArgument != null) {
for (JetExpression expression : arrayArgument.getFirst()) {
checkCompileTimeConstant(expression, arrayArgument.getSecond(), trace);
}
}
}
CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression);
if (constant != null && constant.canBeUsedInAnnotations()) {
return;
}
ClassifierDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression));
}
else if (descriptor instanceof ClassDescriptor && AnnotationUtils.isJavaLangClass((ClassDescriptor) descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CLASS_LITERAL.on(argumentExpression));
}
else {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression));
}
}
@Nullable
private static Pair<List<JetExpression>, JetType> getArgumentExpressionsForArrayCall(
@NotNull JetCallExpression expression,
@NotNull BindingTrace trace
) {
ResolvedCall<?> resolvedCall = trace.get(BindingContext.RESOLVED_CALL, (expression).getCalleeExpression());
if (resolvedCall == null || !AnnotationUtils.isArrayMethodCall(resolvedCall)) {
return null;
}
assert resolvedCall.getValueArguments().size() == 1 : "Array function should have only one vararg parameter";
Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next();
List<JetExpression> result = Lists.newArrayList();
JetType elementType = argumentEntry.getKey().getVarargElementType();
for (ValueArgument valueArgument : argumentEntry.getValue().getArguments()) {
JetExpression valueArgumentExpression = valueArgument.getArgumentExpression();
if (valueArgumentExpression != null) {
if (elementType != null) {
result.add(valueArgumentExpression);
}
}
}
return new Pair<List<JetExpression>, JetType>(result, elementType);
}
private static boolean hasSpread(@NotNull ResolvedValueArgument argument) {
List<ValueArgument> arguments = argument.getArguments();
return arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
@@ -252,24 +322,10 @@ public class AnnotationResolver {
JetType defaultType = ((IntegerValueTypeConstant) constant).getType(expectedType);
ArgumentTypeResolver.updateNumberType(defaultType, argumentExpression, trace);
}
if (constant != null && constant.canBeUsedInAnnotations()) {
if (constant != null) {
constants.add(constant);
}
else {
JetType expressionType = trace.get(BindingContext.EXPRESSION_TYPE, argumentExpression);
if (expressionType != null && expressionType.equals(expectedType)) {
ClassifierDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor();
if (descriptor != null && DescriptorUtils.isEnumClass(descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST.on(argumentExpression));
}
else if (descriptor instanceof ClassDescriptor && AnnotationUtils.isJavaLangClass((ClassDescriptor) descriptor)) {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CLASS_LITERAL.on(argumentExpression));
}
else {
trace.report(Errors.ANNOTATION_PARAMETER_MUST_BE_CONST.on(argumentExpression));
}
}
}
checkCompileTimeConstant(argumentExpression, expectedType, trace);
}
}
return constants;
@@ -0,0 +1,26 @@
annotation class Ann(val i: IntArray)
Ann(intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>))
Ann(intArray(i2))
Ann(intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>))
Ann(intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>))
Ann(intArray(<!TYPE_MISMATCH!>intArray(i, i2, i3)<!>))
class Test
var i = 1
val i2 = 1
val i3 = foo()
fun foo(): Int = 1
annotation class AnnJC(val i: Array<Class<*>>)
AnnJC(array(javaClass<Test>()))
AnnJC(array(iJC))
class TestJC
val iJC = javaClass<Test>()
annotation class AnnAnn(val i: Array<Ann>)
AnnAnn(array(Ann(intArray(1))))
AnnAnn(array(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>iAnn<!>))
class TestAnn
val iAnn = <!ANNOTATION_CLASS_CONSTRUCTOR_CALL!>Ann(intArray(1))<!>
@@ -0,0 +1,29 @@
annotation class Ann(vararg val i: Int)
Ann(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>)
Ann(i2)
Ann(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>)
Ann(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>)
Ann(*intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>))
Ann(*intArray(i2))
Ann(*intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>))
Ann(*intArray(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>i<!>, i2, <!ANNOTATION_PARAMETER_MUST_BE_CONST!>i3<!>))
class Test
var i = 1
val i2 = 1
val i3 = foo()
fun foo(): Int = 1
annotation class AnnJC(vararg val i: Class<*>)
AnnJC(*array(javaClass<Test>()))
AnnJC(*array(iJC))
class TestJC
val iJC = javaClass<Test>()
annotation class AnnAnn(vararg val i: Ann)
AnnAnn(*array(Ann(1)))
AnnAnn(*array(<!ANNOTATION_PARAMETER_MUST_BE_CONST!>iAnn<!>))
class TestAnn
val iAnn = <!ANNOTATION_CLASS_CONSTRUCTOR_CALL!>Ann(1)<!>
@@ -510,6 +510,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
@TestMetadata("compiler/testData/diagnostics/tests/annotations")
@InnerTestClasses({Annotations.AnnotationParameterMustBeConstant.class})
public static class Annotations extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInAnnotations() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -560,6 +561,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant.kt");
}
@TestMetadata("annotationParameterMustBeConstantVararg.kt")
public void testAnnotationParameterMustBeConstantVararg() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstantVararg.kt");
}
@TestMetadata("annotationParameterMustBeEnumConst.kt")
public void testAnnotationParameterMustBeEnumConst() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeEnumConst.kt");
@@ -645,6 +651,25 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/annotations/onMultiDeclaration.kt");
}
@TestMetadata("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant")
public static class AnnotationParameterMustBeConstant extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInAnnotationParameterMustBeConstant() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/array.kt");
}
}
public static Test innerSuite() {
TestSuite suite = new TestSuite("Annotations");
suite.addTestSuite(Annotations.class);
suite.addTestSuite(AnnotationParameterMustBeConstant.class);
return suite;
}
}
@TestMetadata("compiler/testData/diagnostics/tests/backingField")
@@ -6796,7 +6821,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
public static Test innerSuite() {
TestSuite suite = new TestSuite("Tests");
suite.addTestSuite(Tests.class);
suite.addTestSuite(Annotations.class);
suite.addTest(Annotations.innerSuite());
suite.addTestSuite(BackingField.class);
suite.addTestSuite(CallableReference.class);
suite.addTest(Cast.innerSuite());
@@ -1,4 +1,6 @@
// "class com.intellij.codeInspection.SuppressIntentionAction" "false"
[suppress("FOO"<caret>!!)]
fun foo() {}
[Ann(Integer.MAX_VALUE<caret> + 1)]
fun foo() {}
annotation class Ann(val b: Int)