From 7384d25cb3975a7fdc522ff8f84ab3dadcaba03a Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Fri, 27 Nov 2015 12:57:34 +0300 Subject: [PATCH] Prohibit Array --- .../kotlin/resolve/TargetPlatform.kt | 7 ++- .../jetbrains/kotlin/resolve/TypeResolver.kt | 7 ++- .../CallReturnsArrayOfNothingChecker.kt | 48 +++++++++++++++++ .../testsWithStdLib/ArrayOfNothing.kt | 54 +++++++++++++++++++ .../testsWithStdLib/ArrayOfNothing.txt | 26 +++++++++ .../reified/reifiedNothingSubstitution.kt | 4 +- .../DiagnosticsTestWithStdLibGenerated.java | 6 +++ .../org/jetbrains/kotlin/types/TypeUtils.kt | 7 +++ 8 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallReturnsArrayOfNothingChecker.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 57fb5757a3c..685c35a4d7c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -25,7 +25,10 @@ import org.jetbrains.kotlin.descriptors.ModuleParameters import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.checkers.* -import org.jetbrains.kotlin.resolve.validation.* +import org.jetbrains.kotlin.resolve.validation.DeprecatedSymbolValidator +import org.jetbrains.kotlin.resolve.validation.InfixValidator +import org.jetbrains.kotlin.resolve.validation.OperatorValidator +import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.DynamicTypesSettings @@ -58,7 +61,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( InfixModifierChecker()) private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), - SafeCallChecker(), InvokeConventionChecker()) + SafeCallChecker(), InvokeConventionChecker(), CallReturnsArrayOfNothingChecker()) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator(), InfixValidator()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 47332c1695d..2ac29920c94 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -33,14 +33,15 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.recordScope import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallableDescriptors import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.LazyEntity -import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.Variance.* +import org.jetbrains.kotlin.types.typeUtil.isArrayOfNothing public class TypeResolver( private val annotationResolver: AnnotationResolver, @@ -349,6 +350,10 @@ public class TypeResolver( } } + if (resultingType.isArrayOfNothing()) { + c.trace.report(UNSUPPORTED.on(type, "Array is illegal")) + } + return type(resultingType) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallReturnsArrayOfNothingChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallReturnsArrayOfNothingChecker.kt new file mode 100644 index 00000000000..b49ddb37e26 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CallReturnsArrayOfNothingChecker.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 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.calls.checkers + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.DeferredType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isArrayOfNothing + +class CallReturnsArrayOfNothingChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { + val returnType = resolvedCall.resultingDescriptor.returnType + + if (returnType.containsArrayOfNothing()) { + val callElement = resolvedCall.call.callElement + val diagnostic = Errors.UNSUPPORTED.on(callElement, "Array in return type is illegal") + context.trace.report(diagnostic) + } + } + + private fun KotlinType?.containsArrayOfNothing(): Boolean { + // if this.isComputing is true, it means that resolve + // has run into recursion, so checking for Array is meaningless anyway, + // and error about recursion will be reported later + if (this == null || this is DeferredType && this.isComputing) return false + + if (isArrayOfNothing()) return true + + return arguments.any { !it.isStarProjection && it.type.containsArrayOfNothing() } + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.kt b/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.kt new file mode 100644 index 00000000000..7046e50f6db --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.kt @@ -0,0 +1,54 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -UNCHECKED_CAST -USELESS_CAST +class A + +fun test1( + a: Array, + b: Array, + c: Array, + d: Array, + e: Array, + f: Array +) {} + +fun test2( + a: Array?, + b: Array?, + c: Array?, + d: Array?, + e: Array?, + f: Array? +) {} + +fun test3( + a: A<Array>, + b: A<Array>, + c: A<Array>, + d: A<Array>, + e: A<Array>, + f: A<Array> +) {} + +fun test4( + a: Array>, + b: Array>, + c: Array>, + d: Array>, + e: Array>, + f: Array> +) {} + +fun test5() { + arrayOf() + Array(10) { throw Exception() } +} + +fun foo(): Array = (object {} as Any) as Array + +fun test6() = foo() + + +class B(val array: Array) + +fun bar() = B>(arrayOf()) + +fun test7() = bar() diff --git a/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.txt b/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.txt new file mode 100644 index 00000000000..b905373803d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.txt @@ -0,0 +1,26 @@ +package + +public fun bar(): B> +public fun foo(): kotlin.Array +public fun test1(/*0*/ a: kotlin.Array, /*1*/ b: kotlin.Array, /*2*/ c: kotlin.Array, /*3*/ d: kotlin.Array, /*4*/ e: kotlin.Array, /*5*/ f: kotlin.Array): kotlin.Unit +public fun test2(/*0*/ a: kotlin.Array?, /*1*/ b: kotlin.Array?, /*2*/ c: kotlin.Array?, /*3*/ d: kotlin.Array?, /*4*/ e: kotlin.Array?, /*5*/ f: kotlin.Array?): kotlin.Unit +public fun test3(/*0*/ a: A>, /*1*/ b: A>, /*2*/ c: A>, /*3*/ d: A>, /*4*/ e: A>, /*5*/ f: A>): kotlin.Unit +public fun test4(/*0*/ a: kotlin.Array>, /*1*/ b: kotlin.Array>, /*2*/ c: kotlin.Array>, /*3*/ d: kotlin.Array>, /*4*/ e: kotlin.Array>, /*5*/ f: kotlin.Array>): kotlin.Unit +public fun test5(): kotlin.Unit +public fun test6(): kotlin.Array +public fun test7(): B> + +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 +} + +public final class B { + public constructor B(/*0*/ array: kotlin.Array) + public final val array: kotlin.Array + 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/testData/diagnostics/testsWithStdLib/reified/reifiedNothingSubstitution.kt b/compiler/testData/diagnostics/testsWithStdLib/reified/reifiedNothingSubstitution.kt index f409857d53d..70f3b60482b 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/reified/reifiedNothingSubstitution.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/reified/reifiedNothingSubstitution.kt @@ -3,8 +3,8 @@ inline fun foo(block: () -> T): String = block().toString() fun box() { - val a = arrayOf(null!!) - val b = Array(5) { null!! } + val a = arrayOf(null!!) + val b = Array(5) { null!! } val c = foo() { null!! } val d = foo { null!! } val e = foo { "1" as Nothing } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 6ca79d2990a..0e3176d846b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -35,6 +35,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("ArrayOfNothing.kt") + public void testArrayOfNothing() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/ArrayOfNothing.kt"); + doTest(fileName); + } + @TestMetadata("CallCompanionProtectedNonStatic.kt") public void testCallCompanionProtectedNonStatic() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index ba37ec5a1e7..eaf153ad5d3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -55,6 +55,13 @@ fun KotlinType.isAnyOrNullableAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this) fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this) +fun KotlinType?.isArrayOfNothing(): Boolean { + if (this == null || !KotlinBuiltIns.isArray(this)) return false + + val typeArg = arguments.firstOrNull()?.type + return typeArg != null && KotlinBuiltIns.isNothingOrNullableNothing(typeArg) +} + private fun KotlinType.getContainedTypeParameters(): Collection { val declarationDescriptor = getConstructor().getDeclarationDescriptor() if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)