From fd3f852a93975a66d3e6db5c24e7625580e4c383 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Wed, 22 Jan 2014 13:10:18 +0400 Subject: [PATCH] Annotation parameter must be constant on vararg and array arguments --- .../jet/lang/resolve/AnnotationResolver.java | 98 +++++++++++++++---- .../array.kt | 26 +++++ ...annotationParameterMustBeConstantVararg.kt | 29 ++++++ .../checkers/JetDiagnosticsTestGenerated.java | 27 ++++- .../unavailable/beforeInAnnotationArgument.kt | 6 +- 5 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/array.kt create mode 100644 compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstantVararg.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java index 4b386653dcf..1c77593c0fc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java @@ -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> constants = resolveValueArguments(descriptorToArgument.getValue(), parameterDescriptor.getType(), trace); + boolean argumentsAsVararg = varargElementType != null && !hasSpread(descriptorToArgument.getValue()); + List> 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, 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, 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 argumentEntry = resolvedCall.getValueArguments().entrySet().iterator().next(); + + List 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, JetType>(result, elementType); + } + private static boolean hasSpread(@NotNull ResolvedValueArgument argument) { List 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; diff --git a/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/array.kt b/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/array.kt new file mode 100644 index 00000000000..9d3f2643fda --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstant/array.kt @@ -0,0 +1,26 @@ +annotation class Ann(val i: IntArray) + +Ann(intArray(i)) +Ann(intArray(i2)) +Ann(intArray(i3)) +Ann(intArray(i, i2, i3)) +Ann(intArray(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>) +AnnJC(array(javaClass())) +AnnJC(array(iJC)) +class TestJC +val iJC = javaClass() + +annotation class AnnAnn(val i: Array) +AnnAnn(array(Ann(intArray(1)))) +AnnAnn(array(iAnn)) +class TestAnn +val iAnn = Ann(intArray(1)) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstantVararg.kt b/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstantVararg.kt new file mode 100644 index 00000000000..42f7ccfb73e --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/annotationParameterMustBeConstantVararg.kt @@ -0,0 +1,29 @@ +annotation class Ann(vararg val i: Int) + +Ann(i) +Ann(i2) +Ann(i3) +Ann(i, i2, i3) +Ann(*intArray(i)) +Ann(*intArray(i2)) +Ann(*intArray(i3)) +Ann(*intArray(i, i2, 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())) +AnnJC(*array(iJC)) +class TestJC +val iJC = javaClass() + +annotation class AnnAnn(vararg val i: Ann) +AnnAnn(*array(Ann(1))) +AnnAnn(*array(iAnn)) +class TestAnn +val iAnn = Ann(1) \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index b118457bf60..124afcda310 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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()); diff --git a/idea/testData/quickfix/suppress/forStatement/unavailable/beforeInAnnotationArgument.kt b/idea/testData/quickfix/suppress/forStatement/unavailable/beforeInAnnotationArgument.kt index c0eacffc21f..afa779f2161 100644 --- a/idea/testData/quickfix/suppress/forStatement/unavailable/beforeInAnnotationArgument.kt +++ b/idea/testData/quickfix/suppress/forStatement/unavailable/beforeInAnnotationArgument.kt @@ -1,4 +1,6 @@ // "class com.intellij.codeInspection.SuppressIntentionAction" "false" -[suppress("FOO"!!)] -fun foo() {} \ No newline at end of file +[Ann(Integer.MAX_VALUE + 1)] +fun foo() {} + +annotation class Ann(val b: Int) \ No newline at end of file