Refactor and improve class literal type checking code
Infer something sensible instead of error types when an error is reported, such as absence of a type argument for Array or presence of type arguments for other types
This commit is contained in:
+29
-43
@@ -72,65 +72,51 @@ class DoubleColonExpressionResolver(
|
||||
)
|
||||
val possiblyBareType = typeResolver.resolvePossiblyBareType(context, expression.typeReference!!)
|
||||
|
||||
var type: KotlinType? = null
|
||||
if (!possiblyBareType.isBare && possiblyBareType.actualType.isError) {
|
||||
return null
|
||||
}
|
||||
|
||||
var reportError = false
|
||||
val type: KotlinType
|
||||
if (possiblyBareType.isBare) {
|
||||
if (!possiblyBareType.isNullable) {
|
||||
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor
|
||||
if (descriptor is ClassDescriptor) {
|
||||
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
|
||||
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
|
||||
return null
|
||||
}
|
||||
type = KotlinTypeImpl.create(
|
||||
Annotations.EMPTY, descriptor, false, descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
|
||||
)
|
||||
}
|
||||
val descriptor = possiblyBareType.bareTypeConstructor.declarationDescriptor as? ClassDescriptor
|
||||
?: error("Only classes can produce bare types: $possiblyBareType")
|
||||
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
|
||||
context.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression))
|
||||
}
|
||||
|
||||
type = KotlinTypeImpl.create(
|
||||
Annotations.EMPTY, descriptor, possiblyBareType.isNullable,
|
||||
descriptor.typeConstructor.parameters.map(TypeUtils::makeStarProjection)
|
||||
)
|
||||
}
|
||||
else {
|
||||
val actualType = possiblyBareType.actualType
|
||||
if (actualType.isError) return null
|
||||
if (isAllowedInClassLiteral(actualType)) {
|
||||
type = actualType
|
||||
}
|
||||
type = possiblyBareType.actualType
|
||||
reportError = !isAllowedInClassLiteral(type)
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
return type
|
||||
if (type.isMarkedNullable || reportError) {
|
||||
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
|
||||
}
|
||||
|
||||
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression))
|
||||
return null
|
||||
return type
|
||||
}
|
||||
|
||||
private fun isAllowedInClassLiteral(type: KotlinType): Boolean =
|
||||
isClassifierAvailableAtRuntime(type, false)
|
||||
|
||||
private fun isClassifierAvailableAtRuntime(type: KotlinType, canBeNullable: Boolean): Boolean {
|
||||
if (type.isMarkedNullable && !canBeNullable) return false
|
||||
|
||||
private fun isAllowedInClassLiteral(type: KotlinType): Boolean {
|
||||
val typeConstructor = type.constructor
|
||||
val typeDeclarationDescriptor = typeConstructor.declarationDescriptor
|
||||
val typeIsArray = KotlinBuiltIns.isArray(type)
|
||||
val descriptor = typeConstructor.declarationDescriptor
|
||||
|
||||
when (typeDeclarationDescriptor) {
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> {
|
||||
val parameters = typeConstructor.parameters
|
||||
if (parameters.size != type.arguments.size) return false
|
||||
|
||||
val typeArgumentsIterator = type.arguments.iterator()
|
||||
for (parameter in parameters) {
|
||||
if (!typeIsArray && !parameter.isReified) return false
|
||||
|
||||
val typeArgument = typeArgumentsIterator.next() ?: return false
|
||||
|
||||
if (typeArgument.isStarProjection) return false
|
||||
if (!isClassifierAvailableAtRuntime(typeArgument.type, true)) return false
|
||||
if (KotlinBuiltIns.isNonPrimitiveArray(descriptor)) {
|
||||
return type.arguments.none { typeArgument ->
|
||||
typeArgument.isStarProjection || !isAllowedInClassLiteral(typeArgument.type)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return type.arguments.isEmpty()
|
||||
}
|
||||
is TypeParameterDescriptor -> return typeDeclarationDescriptor.isReified
|
||||
is TypeParameterDescriptor -> return descriptor.isReified
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,5 @@ val a02 = Array<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Array<!>>::class
|
||||
val a03 = Array<Any?>::class
|
||||
val a04 = Array<Array<Any?>?>::class
|
||||
val a05 = Array<IntArray?>::class
|
||||
val a06 = <!ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT!>kotlin.Array::class<!>
|
||||
val a07 = kotlin.Array<IntArray?>::class
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package
|
||||
|
||||
public val a01: [ERROR : Unresolved class]
|
||||
public val a01: kotlin.reflect.KClass<kotlin.Array<*>>
|
||||
public val a02: kotlin.reflect.KClass<kotlin.Array<[ERROR : Array]>>
|
||||
public val a03: kotlin.reflect.KClass<kotlin.Array<kotlin.Any?>>
|
||||
public val a04: kotlin.reflect.KClass<kotlin.Array<kotlin.Array<kotlin.Any?>?>>
|
||||
public val a05: kotlin.reflect.KClass<kotlin.Array<kotlin.IntArray?>>
|
||||
public val a06: kotlin.reflect.KClass<kotlin.Array<*>>
|
||||
public val a07: kotlin.reflect.KClass<kotlin.Array<kotlin.IntArray?>>
|
||||
|
||||
@@ -4,5 +4,8 @@ fun <T> f1(): KClass<Array<T>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<T>::clas
|
||||
fun <T> f2(): KClass<Array<Array<T>>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<Array<T>>::class<!>
|
||||
inline fun <reified T> f3() = Array<T>::class
|
||||
inline fun <reified T> f4() = Array<Array<T>>::class
|
||||
fun f5(): KClass<Array<Any>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<*>::class<!>
|
||||
fun f6(): KClass<Array<Int?>> = Array<Int?>::class
|
||||
fun f5(): KClass<Array<Any>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS, TYPE_MISMATCH!>Array<*>::class<!>
|
||||
fun f6(): KClass<Array<Int?>> = Array<Int?>::class
|
||||
fun f7() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<String>>::class<!>
|
||||
fun f8() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<String>?>::class<!>
|
||||
fun f9() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<*>?>::class<!>
|
||||
|
||||
@@ -6,3 +6,6 @@ public inline fun </*0*/ reified T> f3(): kotlin.reflect.KClass<kotlin.Array<T>>
|
||||
public inline fun </*0*/ reified T> f4(): kotlin.reflect.KClass<kotlin.Array<kotlin.Array<T>>>
|
||||
public fun f5(): kotlin.reflect.KClass<kotlin.Array<kotlin.Any>>
|
||||
public fun f6(): kotlin.reflect.KClass<kotlin.Array<kotlin.Int?>>
|
||||
public fun f7(): kotlin.reflect.KClass<kotlin.Array<kotlin.collections.List<kotlin.String>>>
|
||||
public fun f8(): kotlin.reflect.KClass<kotlin.Array<kotlin.collections.List<kotlin.String>?>>
|
||||
public fun f9(): kotlin.reflect.KClass<kotlin.Array<kotlin.collections.List<*>?>>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package
|
||||
|
||||
public val a1: kotlin.reflect.KClass<A<*>>
|
||||
public val a2: [ERROR : Unresolved class]
|
||||
public val a3: [ERROR : Unresolved class]
|
||||
public val a4: [ERROR : Unresolved class]
|
||||
public val a2: kotlin.reflect.KClass<A<*>>
|
||||
public val a3: kotlin.reflect.KClass<A<kotlin.String>>
|
||||
public val a4: kotlin.reflect.KClass<A<out kotlin.String?>>
|
||||
public val b1: kotlin.reflect.KClass<kotlin.Int>
|
||||
public val b2: kotlin.reflect.KClass<kotlin.Nothing>
|
||||
public val i1: kotlin.reflect.KClass<A<*>.Inner<*>>
|
||||
public val i2: [ERROR : Unresolved class]
|
||||
public val i3: [ERROR : Unresolved class]
|
||||
public val i2: kotlin.reflect.KClass<A<*>.Inner<*>>
|
||||
public val i3: kotlin.reflect.KClass<A<kotlin.Int>.Inner<kotlin.CharSequence>>
|
||||
public val m1: kotlin.reflect.KClass<kotlin.collections.Map<*, *>>
|
||||
public val m2: [ERROR : Unresolved class]
|
||||
public val m2: kotlin.reflect.KClass<kotlin.collections.Map<kotlin.Int, *>>
|
||||
public val m3: kotlin.reflect.KClass<kotlin.collections.Map.Entry<*, *>>
|
||||
public val n1: kotlin.reflect.KClass<A.Nested<*>>
|
||||
public val n2: [ERROR : Unresolved class]
|
||||
public val n2: kotlin.reflect.KClass<A.Nested<*>>
|
||||
|
||||
public final class A</*0*/ T> {
|
||||
public constructor A</*0*/ T>()
|
||||
|
||||
@@ -13,4 +13,8 @@ fun <T : Any> foo() {
|
||||
val t2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T?::class<!>
|
||||
}
|
||||
|
||||
val m = Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>::class
|
||||
inline fun <reified T : Any> bar() {
|
||||
val t3 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T?::class<!>
|
||||
}
|
||||
|
||||
val m = Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>::class
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package
|
||||
|
||||
public val a1: [ERROR : Unresolved class]
|
||||
public val a2: [ERROR : Unresolved class]
|
||||
public val l1: [ERROR : Unresolved class]
|
||||
public val l2: [ERROR : Unresolved class]
|
||||
public val a1: kotlin.reflect.KClass<A?>
|
||||
public val a2: kotlin.reflect.KClass<A?>
|
||||
public val l1: kotlin.reflect.KClass<kotlin.collections.List<kotlin.String>?>
|
||||
public val l2: kotlin.reflect.KClass<kotlin.collections.List<*>?>
|
||||
public val m: [ERROR : Unresolved class]
|
||||
public inline fun </*0*/ reified T : kotlin.Any> bar(): kotlin.Unit
|
||||
public fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit
|
||||
|
||||
public final class A {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package test.foo.bar
|
||||
|
||||
class A
|
||||
|
||||
val k = test.foo.bar.A::class
|
||||
|
||||
val l = java.lang.Class::class
|
||||
@@ -0,0 +1,19 @@
|
||||
package
|
||||
|
||||
package test {
|
||||
|
||||
package test.foo {
|
||||
|
||||
package test.foo.bar {
|
||||
public val k: kotlin.reflect.KClass<test.foo.bar.A>
|
||||
public val l: kotlin.reflect.KClass<java.lang.Class<*>>
|
||||
|
||||
public final class A {
|
||||
public constructor A()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2727,6 +2727,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedClassLiteral.kt")
|
||||
public void testQualifiedClassLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleClassLiteral.kt")
|
||||
public void testSimpleClassLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/simpleClassLiteral.kt");
|
||||
|
||||
Reference in New Issue
Block a user