Prohibit type parameters in class literals in annotation arguments
#KT-27799 Fixed
This commit is contained in:
@@ -231,6 +231,8 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_CONST = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_CONST = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER = DiagnosticFactory0.create(WARNING);
|
||||||
|
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING);
|
||||||
DiagnosticFactory0<KtAnnotationEntry> ANNOTATION_USED_AS_ANNOTATION_ARGUMENT = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtAnnotationEntry> ANNOTATION_USED_AS_ANNOTATION_ARGUMENT = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+2
@@ -866,6 +866,8 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_CONST, "An annotation argument must be a compile-time constant");
|
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_CONST, "An annotation argument must be a compile-time constant");
|
||||||
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST, "An enum annotation argument must be a enum constant");
|
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST, "An enum annotation argument must be a enum constant");
|
||||||
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL, "An annotation argument must be a class literal (T::class)");
|
MAP.put(ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL, "An annotation argument must be a class literal (T::class)");
|
||||||
|
MAP.put(ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER, "Type parameter in a class literal is not allowed in an annotation argument");
|
||||||
|
MAP.put(ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR, "Type parameter in a class literal is deprecated in an annotation argument");
|
||||||
MAP.put(ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, "Default value of annotation parameter must be a compile-time constant");
|
MAP.put(ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, "Default value of annotation parameter must be a compile-time constant");
|
||||||
|
|
||||||
MAP.put(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE,
|
MAP.put(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE,
|
||||||
|
|||||||
+34
-14
@@ -127,7 +127,13 @@ class ConstantExpressionEvaluator(
|
|||||||
val descriptor = expressionType.constructor.declarationDescriptor
|
val descriptor = expressionType.constructor.declarationDescriptor
|
||||||
val diagnosticFactory = when {
|
val diagnosticFactory = when {
|
||||||
DescriptorUtils.isEnumClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST
|
DescriptorUtils.isEnumClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_ENUM_CONST
|
||||||
descriptor is ClassDescriptor && KotlinBuiltIns.isKClass(descriptor) -> Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
|
descriptor is ClassDescriptor && KotlinBuiltIns.isKClass(descriptor) -> {
|
||||||
|
if (isTypeParameterOrArrayOfTypeParameter(expressionType.arguments.singleOrNull()?.type)) {
|
||||||
|
Errors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR
|
||||||
|
} else {
|
||||||
|
Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> Errors.ANNOTATION_ARGUMENT_MUST_BE_CONST
|
else -> Errors.ANNOTATION_ARGUMENT_MUST_BE_CONST
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +183,8 @@ class ConstantExpressionEvaluator(
|
|||||||
} else {
|
} else {
|
||||||
trace.report(Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL.on(argumentExpression))
|
trace.report(Errors.ANNOTATION_ARGUMENT_MUST_BE_KCLASS_LITERAL.on(argumentExpression))
|
||||||
}
|
}
|
||||||
|
} else if (doubleColonLhs is DoubleColonLHS.Type && isTypeParameterOrArrayOfTypeParameter(doubleColonLhs.type)) {
|
||||||
|
trace.report(Errors.ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER.on(argumentExpression))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,6 +332,13 @@ class ConstantExpressionEvaluator(
|
|||||||
fun getPossiblyErrorConstant(expression: KtExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
|
fun getPossiblyErrorConstant(expression: KtExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
|
||||||
return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression)
|
return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun isTypeParameterOrArrayOfTypeParameter(type: KotlinType?): Boolean =
|
||||||
|
when {
|
||||||
|
type == null -> false
|
||||||
|
KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type)
|
||||||
|
else -> type.constructor.declarationDescriptor is TypeParameterDescriptor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +351,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
private val constantExpressionEvaluator: ConstantExpressionEvaluator,
|
private val constantExpressionEvaluator: ConstantExpressionEvaluator,
|
||||||
private val trace: BindingTrace
|
private val trace: BindingTrace
|
||||||
) : KtVisitor<CompileTimeConstant<*>?, KotlinType>() {
|
) : KtVisitor<CompileTimeConstant<*>?, KotlinType>() {
|
||||||
|
private val languageVersionSettings = constantExpressionEvaluator.languageVersionSettings
|
||||||
private val builtIns = constantExpressionEvaluator.module.builtIns
|
private val builtIns = constantExpressionEvaluator.module.builtIns
|
||||||
|
|
||||||
fun evaluate(expression: KtExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
fun evaluate(expression: KtExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||||
@@ -613,7 +629,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
|
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
|
||||||
|
|
||||||
if ((isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) ||
|
if ((isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) ||
|
||||||
!constantExpressionEvaluator.languageVersionSettings.supportsFeature(LanguageFeature.DivisionByZeroInConstantExpressions)) {
|
!languageVersionSettings.supportsFeature(LanguageFeature.DivisionByZeroInConstantExpressions)
|
||||||
|
) {
|
||||||
return ErrorValue.create("Division by zero").wrap()
|
return ErrorValue.create("Division by zero").wrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -883,11 +900,19 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||||
val type = trace.getType(expression)!!
|
val kClassType = trace.getType(expression)!!
|
||||||
if (type.isError) return null
|
if (kClassType.isError) return null
|
||||||
val descriptor = type.constructor.declarationDescriptor
|
val descriptor = kClassType.constructor.declarationDescriptor
|
||||||
if (descriptor !is ClassDescriptor || !KotlinBuiltIns.isKClass(descriptor)) return null
|
if (descriptor !is ClassDescriptor || !KotlinBuiltIns.isKClass(descriptor)) return null
|
||||||
return KClassValue.create(type.arguments.first().type)?.wrap()
|
|
||||||
|
val type = kClassType.arguments.singleOrNull()?.type ?: return null
|
||||||
|
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitTypeParametersInClassLiteralsInAnnotationArguments) &&
|
||||||
|
ConstantExpressionEvaluator.isTypeParameterOrArrayOfTypeParameter(type)
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return KClassValue.create(type)?.wrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: KotlinType): List<CompileTimeConstant<*>?> {
|
private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: KotlinType): List<CompileTimeConstant<*>?> {
|
||||||
@@ -983,12 +1008,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
parameters: CompileTimeConstant.Parameters,
|
parameters: CompileTimeConstant.Parameters,
|
||||||
expectedType: KotlinType
|
expectedType: KotlinType
|
||||||
): CompileTimeConstant<*>? {
|
): CompileTimeConstant<*>? {
|
||||||
if (parameters.isUnsignedNumberLiteral &&
|
if (parameters.isUnsignedNumberLiteral && !checkAccessibilityOfUnsignedTypes()) {
|
||||||
!checkAccessibilityOfUnsignedTypes(
|
|
||||||
constantExpressionEvaluator.module,
|
|
||||||
constantExpressionEvaluator.languageVersionSettings
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return UnsignedErrorValueTypeConstant(value, parameters)
|
return UnsignedErrorValueTypeConstant(value, parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,8 +1039,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
}.wrap(parameters)
|
}.wrap(parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAccessibilityOfUnsignedTypes(module: ModuleDescriptor, languageVersionSettings: LanguageVersionSettings): Boolean {
|
private fun checkAccessibilityOfUnsignedTypes(): Boolean {
|
||||||
val uInt = module.findClassAcrossModuleDependencies(KotlinBuiltIns.FQ_NAMES.uInt) ?: return false
|
val uInt = constantExpressionEvaluator.module.findClassAcrossModuleDependencies(KotlinBuiltIns.FQ_NAMES.uInt) ?: return false
|
||||||
val accessibility = uInt.checkSinceKotlinVersionAccessibility(languageVersionSettings)
|
val accessibility = uInt.checkSinceKotlinVersionAccessibility(languageVersionSettings)
|
||||||
// Case `NotAccessibleButWasExperimental` will be checked later in `checkExperimentalityOfConstantLiteral`
|
// Case `NotAccessibleButWasExperimental` will be checked later in `checkExperimentalityOfConstantLiteral`
|
||||||
return accessibility is SinceKotlinAccessibility.Accessible
|
return accessibility is SinceKotlinAccessibility.Accessible
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// WITH_REFLECT
|
// !LANGUAGE: +ProhibitTypeParametersInClassLiteralsInAnnotationArguments
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@@ -35,4 +35,11 @@ fun test7() {}
|
|||||||
fun test8() {}
|
fun test8() {}
|
||||||
|
|
||||||
inline val <reified T> T.test9
|
inline val <reified T> T.test9
|
||||||
get() = @Ann(T::class) object {}
|
get() = @AnnArray(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>arrayOf(
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>,
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<T>::class<!>,
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>Array<Array<Array<T>>>::class<!>
|
||||||
|
)<!>) object {}
|
||||||
|
|
||||||
|
inline val <reified T> T.test10
|
||||||
|
get() = @AnnArray(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>[<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER_ERROR!>T::class<!>]<!>) object {}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
|
public val </*0*/ reified T> T.test10: kotlin.Any
|
||||||
public val </*0*/ reified T> T.test9: kotlin.Any
|
public val </*0*/ reified T> T.test9: kotlin.Any
|
||||||
public fun foo(): kotlin.String
|
public fun foo(): kotlin.String
|
||||||
@Ann(k = kotlin.String::class) public fun test1(): kotlin.Unit
|
@Ann(k = kotlin.String::class) public fun test1(): kotlin.Unit
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// !LANGUAGE: -ProhibitTypeParametersInClassLiteralsInAnnotationArguments
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
annotation class Ann(vararg val k: KClass<*>)
|
||||||
|
|
||||||
|
inline val <reified T> T.test
|
||||||
|
get() = @Ann(
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>T::class<!>,
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>Array<T>::class<!>,
|
||||||
|
<!ANNOTATION_ARGUMENT_KCLASS_LITERAL_OF_TYPE_PARAMETER!>Array<Array<Array<T>>>::class<!>
|
||||||
|
) object {}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val </*0*/ reified T> T.test: kotlin.Any
|
||||||
|
|
||||||
|
public final annotation class Ann : kotlin.Annotation {
|
||||||
|
public constructor Ann(/*0*/ vararg k: kotlin.reflect.KClass<*> /*kotlin.Array<out kotlin.reflect.KClass<*>>*/)
|
||||||
|
public final val k: kotlin.Array<out kotlin.reflect.KClass<*>>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -2991,6 +2991,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
|
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inAnnotationArguments_noTypeParams.kt")
|
||||||
|
public void testInAnnotationArguments_noTypeParams() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments_noTypeParams.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("integerValueType.kt")
|
@TestMetadata("integerValueType.kt")
|
||||||
public void testIntegerValueType() throws Exception {
|
public void testIntegerValueType() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
|
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
|
||||||
|
|||||||
Generated
+5
@@ -2986,6 +2986,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
|||||||
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
|
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inAnnotationArguments_noTypeParams.kt")
|
||||||
|
public void testInAnnotationArguments_noTypeParams() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/classLiteral/inAnnotationArguments_noTypeParams.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("integerValueType.kt")
|
@TestMetadata("integerValueType.kt")
|
||||||
public void testIntegerValueType() throws Exception {
|
public void testIntegerValueType() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
|
runTest("compiler/testData/diagnostics/tests/classLiteral/integerValueType.kt");
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ enum class LanguageFeature(
|
|||||||
ProperInlineFromHigherPlatformDiagnostic(KOTLIN_1_4, kind = BUG_FIX),
|
ProperInlineFromHigherPlatformDiagnostic(KOTLIN_1_4, kind = BUG_FIX),
|
||||||
ProhibitRepeatedUseSiteTargetAnnotations(KOTLIN_1_4, kind = BUG_FIX),
|
ProhibitRepeatedUseSiteTargetAnnotations(KOTLIN_1_4, kind = BUG_FIX),
|
||||||
ProhibitUseSiteTargetAnnotationsOnSuperTypes(KOTLIN_1_4, kind = BUG_FIX),
|
ProhibitUseSiteTargetAnnotationsOnSuperTypes(KOTLIN_1_4, kind = BUG_FIX),
|
||||||
|
ProhibitTypeParametersInClassLiteralsInAnnotationArguments(KOTLIN_1_4, kind = BUG_FIX),
|
||||||
|
|
||||||
ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX),
|
ProperVisibilityForCompanionObjectInstanceField(sinceVersion = null, kind = BUG_FIX),
|
||||||
// Temporarily disabled, see KT-27084/KT-22379
|
// Temporarily disabled, see KT-27084/KT-22379
|
||||||
|
|||||||
@@ -214,10 +214,10 @@ class KClassValue(value: Value) : ConstantValue<KClassValue.Value>(value) {
|
|||||||
KClassValue(classId, arrayDimensions)
|
KClassValue(classId, arrayDimensions)
|
||||||
}
|
}
|
||||||
is TypeParameterDescriptor -> {
|
is TypeParameterDescriptor -> {
|
||||||
// This is possible if a reified type parameter is used in annotation on a local class / anonymous object.
|
// This is possible before 1.4 if a reified type parameter is used in annotation on a local class / anonymous object.
|
||||||
// In JVM class file, we can't represent such literal properly, so we're writing java.lang.Object instead.
|
// In JVM class file, we can't represent such literal properly, so we're writing java.lang.Object instead.
|
||||||
// This has no effect on the compiler front-end or other back-ends, so we use kotlin.Any for simplicity here.
|
// This has no effect on the compiler front-end or other back-ends, so we use kotlin.Any for simplicity here.
|
||||||
// Overall, it looks like such code should be disallowed: https://youtrack.jetbrains.com/issue/KT-27799
|
// See LanguageFeature.ProhibitTypeParametersInClassLiteralsInAnnotationArguments
|
||||||
KClassValue(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.any.toSafe()), 0)
|
KClassValue(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.any.toSafe()), 0)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
|
|||||||
Reference in New Issue
Block a user