From ca7e4496c7e7d8293b4d4a85b8f7d7f775d7e8b1 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 21 Apr 2016 18:39:05 +0300 Subject: [PATCH] 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 --- .../DoubleColonExpressionResolver.kt | 72 ++++++++----------- .../diagnostics/tests/classLiteral/arrays.kt | 2 + .../diagnostics/tests/classLiteral/arrays.txt | 4 +- .../tests/classLiteral/genericArrays.kt | 7 +- .../tests/classLiteral/genericArrays.txt | 3 + .../tests/classLiteral/genericClasses.txt | 14 ++-- .../tests/classLiteral/nonClassesOnLHS.kt | 6 +- .../tests/classLiteral/nonClassesOnLHS.txt | 9 +-- .../classLiteral/qualifiedClassLiteral.kt | 7 ++ .../classLiteral/qualifiedClassLiteral.txt | 19 +++++ .../checkers/DiagnosticsTestGenerated.java | 6 ++ 11 files changed, 91 insertions(+), 58 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.kt create mode 100644 compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 092b3e9affe..f5f478c1b84 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -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 } } diff --git a/compiler/testData/diagnostics/tests/classLiteral/arrays.kt b/compiler/testData/diagnostics/tests/classLiteral/arrays.kt index 5abe3339e9c..fd965ff4f25 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/arrays.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/arrays.kt @@ -3,3 +3,5 @@ val a02 = Array<Array>::class val a03 = Array::class val a04 = Array?>::class val a05 = Array::class +val a06 = kotlin.Array::class +val a07 = kotlin.Array::class diff --git a/compiler/testData/diagnostics/tests/classLiteral/arrays.txt b/compiler/testData/diagnostics/tests/classLiteral/arrays.txt index 246e277460e..9a31b37a57a 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/arrays.txt +++ b/compiler/testData/diagnostics/tests/classLiteral/arrays.txt @@ -1,7 +1,9 @@ package -public val a01: [ERROR : Unresolved class] +public val a01: kotlin.reflect.KClass> public val a02: kotlin.reflect.KClass> public val a03: kotlin.reflect.KClass> public val a04: kotlin.reflect.KClass?>> public val a05: kotlin.reflect.KClass> +public val a06: kotlin.reflect.KClass> +public val a07: kotlin.reflect.KClass> diff --git a/compiler/testData/diagnostics/tests/classLiteral/genericArrays.kt b/compiler/testData/diagnostics/tests/classLiteral/genericArrays.kt index a50af8aebbc..94fc428272b 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/genericArrays.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/genericArrays.kt @@ -4,5 +4,8 @@ fun f1(): KClass> = Array::clas fun f2(): KClass>> = Array>::class inline fun f3() = Array::class inline fun f4() = Array>::class -fun f5(): KClass> = Array<*>::class -fun f6(): KClass> = Array::class \ No newline at end of file +fun f5(): KClass> = Array<*>::class +fun f6(): KClass> = Array::class +fun f7() = Array>::class +fun f8() = Array?>::class +fun f9() = Array?>::class diff --git a/compiler/testData/diagnostics/tests/classLiteral/genericArrays.txt b/compiler/testData/diagnostics/tests/classLiteral/genericArrays.txt index db9255fe89e..60c3cd2d383 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/genericArrays.txt +++ b/compiler/testData/diagnostics/tests/classLiteral/genericArrays.txt @@ -6,3 +6,6 @@ public inline fun f3(): kotlin.reflect.KClass> public inline fun f4(): kotlin.reflect.KClass>> public fun f5(): kotlin.reflect.KClass> public fun f6(): kotlin.reflect.KClass> +public fun f7(): kotlin.reflect.KClass>> +public fun f8(): kotlin.reflect.KClass?>> +public fun f9(): kotlin.reflect.KClass?>> diff --git a/compiler/testData/diagnostics/tests/classLiteral/genericClasses.txt b/compiler/testData/diagnostics/tests/classLiteral/genericClasses.txt index cdb5fb2ee7f..16d9c61c27e 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/genericClasses.txt +++ b/compiler/testData/diagnostics/tests/classLiteral/genericClasses.txt @@ -1,19 +1,19 @@ package public val a1: kotlin.reflect.KClass> -public val a2: [ERROR : Unresolved class] -public val a3: [ERROR : Unresolved class] -public val a4: [ERROR : Unresolved class] +public val a2: kotlin.reflect.KClass> +public val a3: kotlin.reflect.KClass> +public val a4: kotlin.reflect.KClass> public val b1: kotlin.reflect.KClass public val b2: kotlin.reflect.KClass public val i1: kotlin.reflect.KClass.Inner<*>> -public val i2: [ERROR : Unresolved class] -public val i3: [ERROR : Unresolved class] +public val i2: kotlin.reflect.KClass.Inner<*>> +public val i3: kotlin.reflect.KClass.Inner> public val m1: kotlin.reflect.KClass> -public val m2: [ERROR : Unresolved class] +public val m2: kotlin.reflect.KClass> public val m3: kotlin.reflect.KClass> public val n1: kotlin.reflect.KClass> -public val n2: [ERROR : Unresolved class] +public val n2: kotlin.reflect.KClass> public final class A { public constructor A() diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt index 62100c2fe04..72161dcc101 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt @@ -13,4 +13,8 @@ fun foo() { val t2 = T?::class } -val m = Map::class \ No newline at end of file +inline fun bar() { + val t3 = T?::class +} + +val m = Map::class diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.txt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.txt index e7c9cd374bd..5b581f99da6 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.txt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.txt @@ -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 +public val a2: kotlin.reflect.KClass +public val l1: kotlin.reflect.KClass?> +public val l2: kotlin.reflect.KClass?> public val m: [ERROR : Unresolved class] +public inline fun bar(): kotlin.Unit public fun foo(): kotlin.Unit public final class A { diff --git a/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.kt b/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.kt new file mode 100644 index 00000000000..d44fdcba193 --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.kt @@ -0,0 +1,7 @@ +package test.foo.bar + +class A + +val k = test.foo.bar.A::class + +val l = java.lang.Class::class diff --git a/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.txt b/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.txt new file mode 100644 index 00000000000..57859c61ae0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/qualifiedClassLiteral.txt @@ -0,0 +1,19 @@ +package + +package test { + + package test.foo { + + package test.foo.bar { + public val k: kotlin.reflect.KClass + public val l: kotlin.reflect.KClass> + + 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 + } + } + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 2168ad977ef..881594861e4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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");