diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index fb577259e3f..ed4de016321 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -114,6 +114,7 @@ public interface Errors { DiagnosticFactory1 NOT_AN_ANNOTATION_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 ANNOTATION_CLASS_WITH_BODY = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INVALID_TYPE_OF_ANNOTATION_MEMBER = DiagnosticFactory0.create(ERROR); // Classes and traits diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index 1303f0fd170..b578856dff6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -434,6 +434,7 @@ public class DefaultErrorMessages { MAP.put(NOT_AN_ANNOTATION_CLASS, "''{0}'' is not an annotation class", TO_STRING); MAP.put(ANNOTATION_CLASS_WITH_BODY, "Body is not allowed for annotation class"); + MAP.put(INVALID_TYPE_OF_ANNOTATION_MEMBER, "Invalid type of annotation member"); MAP.put(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE, "An overriding function is not allowed to specify default values for its parameters"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java new file mode 100644 index 00000000000..5ba69ba59c8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.resolve; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.jet.lang.psi.JetTypeReference; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeProjection; +import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; + +import java.util.List; + +import static org.jetbrains.jet.lang.diagnostics.Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER; +import static org.jetbrains.jet.lang.resolve.BindingContext.VALUE_PARAMETER; +import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isAnnotationClass; +import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClass; + +public class AnnotationUtils { + + public static void checkConstructorParametersType(@NotNull List parameters, @NotNull BindingTrace trace) { + for (JetParameter parameter : parameters) { + VariableDescriptor parameterDescriptor = trace.getBindingContext().get(VALUE_PARAMETER, parameter); + if (parameterDescriptor == null) continue; + JetType parameterType = parameterDescriptor.getType(); + if (!isAcceptableTypeForAnnotationParameter(parameterType)) { + JetTypeReference typeReference = parameter.getTypeReference(); + if (typeReference != null) { + trace.report(INVALID_TYPE_OF_ANNOTATION_MEMBER.on(typeReference)); + } + } + } + } + + private static boolean isAcceptableTypeForAnnotationParameter(@NotNull JetType parameterType) { + if (parameterType.isNullable()) { + return false; + } + ClassDescriptor typeDescriptor = TypeUtils.getClassDescriptor(parameterType); + if (typeDescriptor == null) { + return false; + } + + KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); + if (isEnumClass(typeDescriptor) || + isAnnotationClass(typeDescriptor) || + isJavaLangClass(typeDescriptor) || + builtIns.isPrimitiveArray(parameterType) || + builtIns.isPrimitiveType(parameterType) || + builtIns.getStringType().equals(parameterType)) { + return true; + } + + if (builtIns.isArray(parameterType)) { + List arguments = parameterType.getArguments(); + if (arguments.size() == 1) { + JetType arrayType = arguments.get(0).getType(); + if (arrayType.isNullable()) { + return false; + } + ClassDescriptor arrayTypeDescriptor = TypeUtils.getClassDescriptor(arrayType); + if (arrayTypeDescriptor != null) { + return isEnumClass(arrayTypeDescriptor) || + isAnnotationClass(arrayTypeDescriptor) || + isJavaLangClass(arrayTypeDescriptor) || + builtIns.getStringType().equals(arrayType); + } + } + } + return false; + } + + private static boolean isJavaLangClass(ClassDescriptor descriptor) { + return "java.lang.Class".equals(DescriptorUtils.getFQName(descriptor).asString()); + } + + private AnnotationUtils() { + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java index ace7885a16f..24094762ab5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java @@ -1264,7 +1264,7 @@ public class DescriptorResolver { WritableScopeImpl parameterScope = new WritableScopeImpl( scope, constructorDescriptor, new TraceBasedRedeclarationHandler(trace), "Scope with value parameters of a constructor"); parameterScope.changeLockLevel(WritableScope.LockLevel.BOTH); - return constructorDescriptor.initialize( + ConstructorDescriptorImpl constructor = constructorDescriptor.initialize( typeParameters, resolveValueParameters( constructorDescriptor, @@ -1272,6 +1272,10 @@ public class DescriptorResolver { valueParameters, trace), resolveVisibilityFromModifiers(modifierList, getDefaultConstructorVisibility(classDescriptor)), DescriptorUtils.isConstructorOfStaticNestedClass(constructorDescriptor)); + if (isAnnotationClass(classDescriptor)) { + AnnotationUtils.checkConstructorParametersType(valueParameters, trace); + } + return constructor; } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index 03955144b36..733db8c6763 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -289,6 +289,10 @@ public class DescriptorUtils { return isKindOf(descriptor, ClassKind.ENUM_CLASS); } + public static boolean isAnnotationClass(@NotNull DeclarationDescriptor descriptor) { + return isKindOf(descriptor, ClassKind.ANNOTATION_CLASS); + } + public static boolean isClass(@NotNull DeclarationDescriptor descriptor) { return isKindOf(descriptor, ClassKind.CLASS); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java index fa9cd738836..601c8a69a18 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/lang/KotlinBuiltIns.java @@ -883,6 +883,10 @@ public class KotlinBuiltIns { return jetArrayTypeToPrimitiveJetType.containsKey(TypeUtils.makeNotNullable(type)); } + public boolean isPrimitiveType(@NotNull JetType type) { + return primitiveJetTypeToJetArrayType.containsKey(type); + } + // Functions @NotNull diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java index ef003db815d..ea3c35235ac 100644 --- a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.java @@ -1,5 +1,4 @@ @AString(value = Test.vstring) -@AStringNullable(value = Test.vstringNullable) @AChar(value = Test.vchar) @AInt(value = Test.vint) @AByte(value = Test.vbyte) diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt index 0d2f88007c1..02db3e7699f 100644 --- a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationClass.kt @@ -1,5 +1,4 @@ annotation class AString(val value: String) -annotation class AStringNullable(val value: String?) annotation class AChar(val value: Char) annotation class AInt(val value: Int) annotation class AByte(val value: Byte) @@ -11,7 +10,6 @@ class Test { class object { val vstring: String = "Test" - val vstringNullable: String? = "Test" val vchar: Char = 'c' val vint: Int = 10 val vbyte: Byte = 11 diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java index 20cf3bb2061..72eec43fb76 100644 --- a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.java @@ -1,5 +1,4 @@ @AString(value = Test.vstring) -@AStringNullable(value = Test.vstringNullable) @AChar(value = Test.vchar) @AInt(value = Test.vint) @AByte(value = Test.vbyte) diff --git a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt index d90d88de6f3..27df1a79c1b 100644 --- a/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt +++ b/compiler/testData/compileJavaAgainstKotlin/staticFields/AnnotationTrait.kt @@ -1,5 +1,4 @@ annotation class AString(val value: String) -annotation class AStringNullable(val value: String?) annotation class AChar(val value: Char) annotation class AInt(val value: Int) annotation class AByte(val value: Byte) @@ -11,7 +10,6 @@ trait Test { class object { val vstring: String = "Test" - val vstringNullable: String? = "Test" val vchar: Char = 'c' val vint: Int = 10 val vbyte: Byte = 11 diff --git a/compiler/testData/diagnostics/tests/annotations/invalidTypesInAnnotationConstructor.kt b/compiler/testData/diagnostics/tests/annotations/invalidTypesInAnnotationConstructor.kt new file mode 100644 index 00000000000..0f3c448614e --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/invalidTypesInAnnotationConstructor.kt @@ -0,0 +1,72 @@ +package test + +import java.lang.annotation.RetentionPolicy + +// CORRECT +annotation class Ann1(val p1: Int, + val p2: Byte, + val p3: Short, + val p4: Long, + val p5: Double, + val p6: Float, + val p7: Char, + val p8: Boolean) + +annotation class Ann2(val p1: String) +annotation class Ann3(val p1: Ann1) +annotation class Ann4(val p1: IntArray, + val p2: ByteArray, + val p3: ShortArray, + val p4: LongArray, + val p5: DoubleArray, + val p6: FloatArray, + val p7: CharArray, + val p8: BooleanArray) + +annotation class Ann5(val p1: MyEnum) + +annotation class Ann6(val p: Class<*>) +annotation class Ann7(val p: RetentionPolicy) + +annotation class Ann8(val p1: Array, + val p2: Array>, + val p3: Array, + val p4: Array) + +annotation class Ann9(vararg val p1: String, + vararg val p2: Class<*>, + vararg val p3: MyEnum, + vararg val p4: Ann1, + vararg val p5: Int) + +// INCORRECT +annotation class InAnn1(val p1: Int?, + val p3: Short?, + val p4: Long?, + val p5: Double?, + val p6: Float?, + val p7: Char?, + val p8: Boolean?) + +annotation class InAnn4(val p1: Array, + val p2: Array?) + +annotation class InAnn6(val p: Class<*>?) +annotation class InAnn7(val p: RetentionPolicy?) +annotation class InAnn8(val p1: Array, + val p2: Array, + val p3: Array, + val p4: Array) + +annotation class InAnn9(val p: MyClass) + +annotation class InAnn10(val p1: String?) +annotation class InAnn11(val p1: Ann1?) +annotation class InAnn12(val p1: MyEnum?) + + +enum class MyEnum { + A +} + +class MyClass \ 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 8b0f69adf8d..3088039f8df 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -565,6 +565,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/annotations/Deprecated.kt"); } + @TestMetadata("invalidTypesInAnnotationConstructor.kt") + public void testInvalidTypesInAnnotationConstructor() throws Exception { + doTest("compiler/testData/diagnostics/tests/annotations/invalidTypesInAnnotationConstructor.kt"); + } + @TestMetadata("JavaAnnotationConstructors.kt") public void testJavaAnnotationConstructors() throws Exception { doTest("compiler/testData/diagnostics/tests/annotations/JavaAnnotationConstructors.kt");