diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index f7bd4932fd1..09c51ce0fee 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -414,6 +414,11 @@ public interface Errors { DiagnosticFactory1> AMBIGUOUS_ANONYMOUS_TYPE_INFERRED = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 + KCLASS_WITH_NULLABLE_ARGUMENT_IN_SIGNATURE = DiagnosticFactory0.create(ERROR, PositioningStrategies.DECLARATION_NAME); + DiagnosticFactory1 + KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE = DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_NAME); + // Property-specific DiagnosticFactory2 VAR_OVERRIDDEN_BY_VAL = 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 16059fa705a..1b608538b64 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -859,6 +859,9 @@ public class DefaultErrorMessages { MAP.put(DATA_CLASS_NOT_PROPERTY_PARAMETER, "Data class primary constructor must have only property (val / var) parameters"); MAP.put(AMBIGUOUS_ANONYMOUS_TYPE_INFERRED, "Right-hand side has anonymous type. Please specify type explicitly", TO_STRING); + MAP.put(KCLASS_WITH_NULLABLE_ARGUMENT_IN_SIGNATURE, "Please specify type explicitly"); + MAP.put(KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE, + "Please add upper bound Any for type parameter ''{0}'' or specify return type explicitly", NAME); MAP.put(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, "''{0}'' is a member and an extension at the same time. References to such elements are not allowed", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index f37a4ca5391..4d907442565 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -74,7 +74,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( SinceKotlinAnnotationValueChecker, ReifiedTypeParameterAnnotationChecker(), DynamicReceiverChecker, - DelegationChecker() + DelegationChecker(), + KClassWithIncorrectTypeArgumentChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/KClassWithIncorrectTypeArgumentChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/KClassWithIncorrectTypeArgumentChecker.kt new file mode 100644 index 00000000000..02439d8dd62 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/KClassWithIncorrectTypeArgumentChecker.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.contains + +object KClassWithIncorrectTypeArgumentChecker : SimpleDeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, bindingContext: BindingContext) { + if (descriptor !is CallableMemberDescriptor || descriptor.visibility == Visibilities.LOCAL) return + + if (declaration !is KtCallableDeclaration || declaration.typeReference != null) return + + // prevent duplicate reporting + if (descriptor is PropertyAccessorDescriptor) return + + val returnType = descriptor.returnType ?: return + + var typeParameterWithoutNotNullableUpperBound: TypeParameterDescriptor? = null + val thereIsBadKClassType = returnType.contains { + val kClassWithBadArgument = it.isKClassWithBadArgument() + if (kClassWithBadArgument) { + it.arguments.singleOrNull()?.type?.constructor?.declarationDescriptor?.let { + if (it is TypeParameterDescriptor && it.containingDeclaration == descriptor) { + typeParameterWithoutNotNullableUpperBound = it + } + } + } + + kClassWithBadArgument + } + + if (typeParameterWithoutNotNullableUpperBound != null) { + diagnosticHolder.report(Errors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE.on(declaration, typeParameterWithoutNotNullableUpperBound!!)) + } + else if (thereIsBadKClassType) { + diagnosticHolder.report(Errors.KCLASS_WITH_NULLABLE_ARGUMENT_IN_SIGNATURE.on(declaration)) + } + } + + private fun UnwrappedType.isKClassWithBadArgument(): Boolean { + val argumentType = arguments.singleOrNull()?.let { if (it.isStarProjection) null else it.type.unwrap() } ?: return false + val klass = (constructor.declarationDescriptor as? ClassDescriptor) ?: return false + + return KotlinBuiltIns.isKClass(klass) && !KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, argumentType.builtIns.anyType) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt index 08f80ca122d..9e7cbaf7ce7 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt @@ -23,8 +23,8 @@ class Test { fun List.testCallable4(): () -> Unit = b?::foo fun List.testClassLiteral1() = a::class - fun List.testClassLiteral2() = b?::class - fun List.testClassLiteral3() = b::class + fun List.testClassLiteral2() = b?::class + fun List.testClassLiteral3() = b::class fun List.testUnresolved1() = unresolved::foo fun List.testUnresolved2() = a<unresolved>::foo diff --git a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt index 9063b6038c1..0755e989e6f 100644 --- a/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt +++ b/compiler/testData/diagnostics/tests/classLiteral/nonClassesOnLHS.kt @@ -2,11 +2,11 @@ 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 diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt new file mode 100644 index 00000000000..d7f0b42e13d --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -TYPE_PARAMETER_AS_REIFIED -TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER -UNUSED_VARIABLE -UNUSED_PARAMETER + +fun test1() = T::class +fun test2() = T::class + +val test3 = T::class +val test4 get() = T::class + +fun test5() = listOf(T::class) + +fun test6(): kotlin.reflect.KClass<T> = T::class +fun test7(): kotlin.reflect.KClass<*> = T::class +fun test8() = String?::class + +fun listOf(e: T): List = null!! + +fun locals() { + fun test1() = T::class + fun test2() = T::class + + val test3 = L::class + fun test4() = L::class +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.txt b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.txt new file mode 100644 index 00000000000..59e5eb5d553 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.txt @@ -0,0 +1,12 @@ +package + +public val test3: kotlin.reflect.KClass +public val test4: kotlin.reflect.KClass +public fun listOf(/*0*/ e: T): kotlin.collections.List +public fun locals(): kotlin.Unit +public fun test1(): kotlin.reflect.KClass +public fun test2(): kotlin.reflect.KClass +public fun test5(): kotlin.collections.List> +public fun test6(): kotlin.reflect.KClass +public fun test7(): kotlin.reflect.KClass<*> +public fun test8(): kotlin.reflect.KClass diff --git a/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt b/compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences/boundClassLiteral.kt index 80e67613c70..09083d00c18 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 a23d24c82bd..5ee8127c51c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -5434,6 +5434,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("kClassInSignature.kt") + public void testKClassInSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/kClassInSignature.kt"); + doTest(fileName); + } + @TestMetadata("kt1141.kt") public void testKt1141() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/declarationChecks/kt1141.kt"); diff --git a/js/js.translator/testData/box/native/useClassFromInlineFun.kt b/js/js.translator/testData/box/native/useClassFromInlineFun.kt index 03cadb70df6..d4d04b308dd 100644 --- a/js/js.translator/testData/box/native/useClassFromInlineFun.kt +++ b/js/js.translator/testData/box/native/useClassFromInlineFun.kt @@ -9,7 +9,7 @@ external class A { inline fun getA() = A::class inline fun getB() = foo() -inline fun foo() = T::class +inline fun foo() = T::class fun box(): String { if (getA() != A::class) return "fail1"