From 6a352a1da7e58ea3ea324f088ff234df3e65a769 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 16 Jan 2017 12:50:45 +0300 Subject: [PATCH] Report error on nullable type alias in class literal #KT-15736 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../DoubleColonExpressionResolver.kt | 23 +++++++++---------- .../tests/classLiteral/nonClassesOnLHS.kt | 12 +++++----- .../tests/classLiteral/typealiases.kt | 15 ++++++++++++ .../tests/classLiteral/typealiases.txt | 10 ++++++++ .../declarationChecks/kClassInSignature.kt | 4 ++-- .../boundClassLiteral.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 6 +++++ 9 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/classLiteral/typealiases.kt create mode 100644 compiler/testData/diagnostics/tests/classLiteral/typealiases.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index a20ebdf8df2..e4ec364f1d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -630,6 +630,7 @@ public interface Errors { DiagnosticFactory0 CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NULLABLE_TYPE_IN_CLASS_LITERAL_LHS = DiagnosticFactory0.create(ERROR); // Destructuring-declarations diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index ae79a11a113..4f87d7cb439 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -879,6 +879,7 @@ public class DefaultErrorMessages { MAP.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal"); MAP.put(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT, "Array class literal requires a type argument, please specify one in angle brackets"); + MAP.put(NULLABLE_TYPE_IN_CLASS_LITERAL_LHS, "Type in a class literal must not be nullable"); //Inline MAP.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); 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 24e3b9a0f0e..aae10391db8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo +import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.lang.UnsupportedOperationException import java.util.* @@ -110,24 +111,21 @@ class DoubleColonExpressionResolver( } val type = (result as DoubleColonLHS.Type).type - val reportError: Boolean + val descriptor = type.constructor.declarationDescriptor if (result.possiblyBareType.isBare) { - val descriptor = type.constructor.declarationDescriptor if (descriptor is ClassDescriptor && KotlinBuiltIns.isNonPrimitiveArray(descriptor)) { c.trace.report(ARRAY_CLASS_LITERAL_REQUIRES_ARGUMENT.on(expression)) } - reportError = false - } - else { - reportError = !isAllowedInClassLiteral(type) } - - val typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type) - if (type is SimpleType && !type.isMarkedNullable && typeParameterDescriptor != null && !typeParameterDescriptor.isReified) { - c.trace.report(TYPE_PARAMETER_AS_REIFIED.on(expression, typeParameterDescriptor)) + if (type is SimpleType && !type.isMarkedNullable && descriptor is TypeParameterDescriptor && !descriptor.isReified) { + c.trace.report(TYPE_PARAMETER_AS_REIFIED.on(expression, descriptor)) } - else if (type.isMarkedNullable || reportError) { + // Note that "T::class" is allowed for type parameter T without a non-null upper bound + else if ((TypeUtils.isNullableType(type) && descriptor !is TypeParameterDescriptor) || expression.hasQuestionMarks) { + c.trace.report(NULLABLE_TYPE_IN_CLASS_LITERAL_LHS.on(expression)) + } + else if (!result.possiblyBareType.isBare && !isAllowedInClassLiteral(type)) { c.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression)) } } @@ -438,7 +436,8 @@ class DoubleColonExpressionResolver( ) } else { - TypeUtils.makeNullableAsSpecified(possiblyBareType.actualType, doubleColonExpression.hasQuestionMarks) + val actualType = possiblyBareType.actualType + if (doubleColonExpression.hasQuestionMarks) actualType.makeNullable() else actualType } return DoubleColonLHS.Type(type, possiblyBareType) diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt index 0755e989e6f..dcd8c279f63 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt @@ -2,19 +2,19 @@ class A -val a1 = A?::class -val a2 = A??::class +val a1 = A?::class +val a2 = A??::class -val l1 = List?::class -val l2 = List?::class +val l1 = List?::class +val l2 = List?::class fun foo() { val t1 = T::class - val t2 = T?::class + val t2 = T?::class } inline fun bar() { - val t3 = T?::class + val t3 = T?::class } val m = Map::class diff --git a/compiler/testData/diagnostics/tests/classLiteral/typealiases.kt b/compiler/testData/diagnostics/tests/classLiteral/typealiases.kt new file mode 100644 index 00000000000..49690ecb927 --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/typealiases.kt @@ -0,0 +1,15 @@ +typealias TString = String +fun f1() = TString::class + +typealias TNullableString = String? +fun f2() = TNullableString::class + +typealias TNullableTString = TString? +typealias TTNullableTString = TNullableTString +fun f3() = TTNullableTString::class + +inline fun f4(b: Boolean): Any { + typealias X = T + typealias Y = T? + return if (b) X::class else Y::class +} diff --git a/compiler/testData/diagnostics/tests/classLiteral/typealiases.txt b/compiler/testData/diagnostics/tests/classLiteral/typealiases.txt new file mode 100644 index 00000000000..e4ddcc4201a --- /dev/null +++ b/compiler/testData/diagnostics/tests/classLiteral/typealiases.txt @@ -0,0 +1,10 @@ +package + +public fun f1(): kotlin.reflect.KClass +public fun f2(): kotlin.reflect.KClass +public fun f3(): kotlin.reflect.KClass +public inline fun f4(/*0*/ b: kotlin.Boolean): kotlin.Any +public typealias TNullableString = kotlin.String? +public typealias TNullableTString = TString? +public typealias TString = kotlin.String +public typealias TTNullableTString = TNullableTString diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt index d7f0b42e13d..0abcf08f62c 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt @@ -10,7 +10,7 @@ fun test5() = listOf( fun test6(): kotlin.reflect.KClass<T> = T::class fun test7(): kotlin.reflect.KClass<*> = T::class -fun test8() = String?::class +fun test8() = String?::class fun listOf(e: T): List = null!! @@ -20,4 +20,4 @@ fun locals() { val test3 = L::class fun test4() = L::class -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt b/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt index 09083d00c18..8ceca51749a 100644 --- a/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt +++ b/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt @@ -9,6 +9,6 @@ val ok3 = O::class val fail1 = ""::class -val fail2 = String?::class +val fail2 = String?::class val fail3 = (C)::class val fail4 = (C.Companion)::class diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index b17689d6995..4ccea137691 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3064,6 +3064,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("typealiases.kt") + public void testTypealiases() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/typealiases.kt"); + doTest(fileName); + } + @TestMetadata("unresolvedClass.kt") public void testUnresolvedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/classLiteral/unresolvedClass.kt");