Check annotation constructor parameter types

This commit is contained in:
Natalia.Ukhorskaya
2013-07-04 11:25:42 +04:00
parent 96cb7da025
commit 33ddd713b7
12 changed files with 188 additions and 7 deletions
@@ -114,6 +114,7 @@ public interface Errors {
DiagnosticFactory1<JetAnnotationEntry, String> NOT_AN_ANNOTATION_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> ANNOTATION_CLASS_WITH_BODY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetTypeReference> INVALID_TYPE_OF_ANNOTATION_MEMBER = DiagnosticFactory0.create(ERROR);
// Classes and traits
@@ -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");
@@ -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<JetParameter> 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<TypeProjection> 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() {
}
}
@@ -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
@@ -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);
}
@@ -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
@@ -1,5 +1,4 @@
@AString(value = Test.vstring)
@AStringNullable(value = Test.vstringNullable)
@AChar(value = Test.vchar)
@AInt(value = Test.vint)
@AByte(value = Test.vbyte)
@@ -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
@@ -1,5 +1,4 @@
@AString(value = Test.vstring)
@AStringNullable(value = Test.vstringNullable)
@AChar(value = Test.vchar)
@AInt(value = Test.vint)
@AByte(value = Test.vbyte)
@@ -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
@@ -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<String>,
val p2: Array<Class<*>>,
val p3: Array<MyEnum>,
val p4: Array<Ann1>)
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: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Int?<!>,
val p3: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Short?<!>,
val p4: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Long?<!>,
val p5: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Double?<!>,
val p6: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Float?<!>,
val p7: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Char?<!>,
val p8: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Boolean?<!>)
annotation class InAnn4(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int><!>,
val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int>?<!>)
annotation class InAnn6(val p: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*>?<!>)
annotation class InAnn7(val p: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>RetentionPolicy?<!>)
annotation class InAnn8(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int><!>,
val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<Int?><!>,
val p3: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<MyClass><!>,
val p4: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<IntArray><!>)
annotation class InAnn9(val p: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyClass<!>)
annotation class InAnn10(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>String?<!>)
annotation class InAnn11(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Ann1?<!>)
annotation class InAnn12(val p1: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyEnum?<!>)
enum class MyEnum {
A
}
class MyClass
@@ -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");