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:
Alexander Udalov
2016-04-21 18:39:05 +03:00
parent 1b79770121
commit ca7e4496c7
11 changed files with 91 additions and 58 deletions
@@ -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");