Check annotation constructor parameter types
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
+72
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user