Prohibit unsupported suspend operators

contains/get/set operators don't work properly on both backends

Also add box test checking that 'compareTo' operator works just fine

 #KT-16219 Fixed
This commit is contained in:
Denis Zharkov
2017-02-07 18:48:02 +03:00
parent 4921bd822d
commit 80638ebc99
6 changed files with 114 additions and 1 deletions
@@ -84,7 +84,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
ReifiedTypeParameterAnnotationChecker(),
DynamicReceiverChecker,
DelegationChecker(),
KClassWithIncorrectTypeArgumentChecker
KClassWithIncorrectTypeArgumentChecker,
SuspendOperatorsCheckers
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -0,0 +1,47 @@
/*
* 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.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.util.OperatorNameConventions
object SuspendOperatorsCheckers : SimpleDeclarationChecker {
private val UNSUPPORTED_OPERATOR_NAMES = setOf(
OperatorNameConventions.CONTAINS,
OperatorNameConventions.GET, OperatorNameConventions.SET
)
override fun check(
declaration: KtDeclaration,
descriptor: DeclarationDescriptor,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext
) {
if (descriptor is FunctionDescriptor && descriptor.isSuspend && descriptor.isOperator &&
descriptor.name in UNSUPPORTED_OPERATOR_NAMES) {
declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD)?.let {
diagnosticHolder.report(Errors.UNSUPPORTED.on(it, "suspend operator \"${descriptor.name}\""))
}
}
}
}
@@ -44,6 +44,16 @@ class A(val x: String) {
x.resume(Unit)
COROUTINE_SUSPENDED
}
// See KT-16221
// operator suspend fun contains(y: String): Boolean = suspendCoroutineOrReturn { x ->
// x.resume(y == "56")
// COROUTINE_SUSPENDED
// }
operator suspend fun compareTo(y: String): Int = suspendCoroutineOrReturn { x ->
x.resume("56".compareTo(y))
COROUTINE_SUSPENDED
}
}
fun builder(c: suspend () -> Unit) {
@@ -94,6 +104,22 @@ suspend fun foo7() {
if (!y.isIncCalled) throw RuntimeException("fail 8")
}
//suspend fun foo8() {
// if ("1" in a) throw RuntimeException("fail 9")
// if (!("1" !in a)) throw RuntimeException("fail 9")
//
// if ("56" in a) throw RuntimeException("fail 10")
// if (!("56" !in a)) throw RuntimeException("fail 11")
//}
suspend fun checkCompareTo(v: String) = (a < v) == ("56" < v)
suspend fun foo9() {
if (!checkCompareTo("55")) throw RuntimeException("fail 12")
if (!checkCompareTo("56")) throw RuntimeException("fail 13")
if (!checkCompareTo("57")) throw RuntimeException("fail 14")
}
fun box(): String {
builder {
@@ -104,6 +130,8 @@ fun box(): String {
//foo5()
foo6()
foo7()
//foo8()
foo9()
}
return "OK"
@@ -6,6 +6,7 @@ public final class A {
private field isSetValueCalled: boolean
private final @org.jetbrains.annotations.NotNull field x: java.lang.String
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
public final @org.jetbrains.annotations.Nullable method compareTo(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final @org.jetbrains.annotations.Nullable method component1(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final @org.jetbrains.annotations.Nullable method getValue(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.reflect.KProperty, @org.jetbrains.annotations.NotNull p2: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final @org.jetbrains.annotations.NotNull method getX(): java.lang.String
@@ -55,12 +56,14 @@ public final class OperatorsKt {
private static @org.jetbrains.annotations.NotNull field a: A
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method checkCompareTo(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo1(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo2(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo3(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo4(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo6(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo7(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method foo9(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.NotNull method getA(): A
public final static method setA(@org.jetbrains.annotations.NotNull p0: A): void
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
@@ -0,0 +1,28 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// SKIP_TXT
class A {
suspend <!UNSUPPORTED!>operator<!> fun get(x: Int) = 1
suspend <!UNSUPPORTED!>operator<!> fun set(x: Int, v: String) {}
<!UNSUPPORTED!>operator<!> suspend fun contains(y: String): Boolean = true
}
class B
suspend <!UNSUPPORTED!>operator<!> fun B.get(x: Int) =1
suspend <!UNSUPPORTED!>operator<!> fun B.set(x: Int, v: String) {}
<!UNSUPPORTED!>operator<!> suspend fun B.contains(y: String): Boolean = true
class C {
suspend fun get(x: Int) = 1
suspend fun set(x: Int, v: String) {}
suspend fun contains(y: String): Boolean = true
}
class D
suspend fun D.get(x: Int) =1
suspend fun D.set(x: Int, v: String) {}
suspend fun D.contains(y: String): Boolean = true
@@ -773,6 +773,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("operators.kt")
public void testOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/operators.kt");
doTest(fileName);
}
@TestMetadata("suspendApplicability.kt")
public void testSuspendApplicability() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendApplicability.kt");