Introduce 'handleException' operator and relevant checks
This commit is contained in:
@@ -57,9 +57,8 @@ object OperatorModifierChecker {
|
|||||||
|
|
||||||
val checkResult = OperatorChecks.checkOperator(functionDescriptor)
|
val checkResult = OperatorChecks.checkOperator(functionDescriptor)
|
||||||
if (checkResult.isSuccess) {
|
if (checkResult.isSuccess) {
|
||||||
if (functionDescriptor.name == OperatorNameConventions.COROUTINE_HANDLE_RESULT
|
if (functionDescriptor.name in COROUTINE_OPERATOR_NAMES
|
||||||
&& !languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines)
|
&& !languageFeatureSettings.supportsFeature(LanguageFeature.Coroutines)) {
|
||||||
) {
|
|
||||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines))
|
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -73,3 +72,6 @@ object OperatorModifierChecker {
|
|||||||
diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier, errorDescription))
|
diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier, errorDescription))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val COROUTINE_OPERATOR_NAMES =
|
||||||
|
setOf(OperatorNameConventions.COROUTINE_HANDLE_RESULT, OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION)
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ class Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<!UNSUPPORTED_FEATURE!>operator<!> fun handleResult(x: String, y: Continuation<Nothing>) {}
|
<!UNSUPPORTED_FEATURE!>operator<!> fun handleResult(x: String, y: Continuation<Nothing>) {}
|
||||||
|
|
||||||
|
<!UNSUPPORTED_FEATURE!>operator<!> fun handleException(x: Throwable, y: Continuation<Nothing>) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(<!UNSUPPORTED_FEATURE!>coroutine<!> c: Controller.() -> Continuation<Unit>) {
|
fun builder(<!UNSUPPORTED_FEATURE!>coroutine<!> c: Controller.() -> Continuation<Unit>) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public fun builder(/*0*/ coroutine c: Controller.() -> kotlin.coroutines.Continu
|
|||||||
public final class Controller {
|
public final class Controller {
|
||||||
public constructor Controller()
|
public constructor Controller()
|
||||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
public final operator fun handleResult(/*0*/ x: kotlin.String, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
public final operator fun handleResult(/*0*/ x: kotlin.String, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public final suspend fun suspendHere(/*0*/ x: kotlin.coroutines.Continuation<kotlin.String>): kotlin.Unit
|
public final suspend fun suspendHere(/*0*/ x: kotlin.coroutines.Continuation<kotlin.String>): kotlin.Unit
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
class A1 {
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A2 {
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable, y: Int) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A3 {
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable, y: Continuation<Any>) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A4 {
|
||||||
|
operator fun handleException(x: Throwable, y: Continuation<Nothing>) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A5 {
|
||||||
|
// TODO: Allow?
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable, y: Continuation<Nothing>): Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
class A6 {
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable = Exception(), y: Continuation<Nothing>) {}
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun handleException(x: Throwable, vararg y: Continuation<Nothing>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A7 {
|
||||||
|
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun <T> handleException(x: Throwable, y: Continuation<Nothing>) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public final class A1 {
|
||||||
|
public constructor A1()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A2 {
|
||||||
|
public constructor A2()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.Int): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A3 {
|
||||||
|
public constructor A3()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Any>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A4 {
|
||||||
|
public constructor A4()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A5 {
|
||||||
|
public constructor A5()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A6 {
|
||||||
|
public constructor A6()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable, /*1*/ vararg y: kotlin.coroutines.Continuation<kotlin.Nothing> /*kotlin.Array<out kotlin.coroutines.Continuation<kotlin.Nothing>>*/): kotlin.Unit
|
||||||
|
public final operator fun handleException(/*0*/ x: kotlin.Throwable = ..., /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class A7 {
|
||||||
|
public constructor A7()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun </*0*/ T> handleException(/*0*/ x: kotlin.Throwable, /*1*/ y: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -3957,6 +3957,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("wrongHandleException.kt")
|
||||||
|
public void testWrongHandleException() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleException.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("wrongHandleResult.kt")
|
@TestMetadata("wrongHandleResult.kt")
|
||||||
public void testWrongHandleResult() throws Exception {
|
public void testWrongHandleResult() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleResult.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleResult.kt");
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ package org.jetbrains.kotlin.types.typeUtil
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
|
import org.jetbrains.kotlin.resolve.calls.inference.isCaptured
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -56,6 +59,10 @@ fun KotlinType.isNullableAny(): Boolean = KotlinBuiltIns.isNullableAny(this)
|
|||||||
fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this)
|
fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this)
|
||||||
fun KotlinType.isPrimitiveNumberType(): Boolean = KotlinBuiltIns.isPrimitiveType(this) && !isBoolean()
|
fun KotlinType.isPrimitiveNumberType(): Boolean = KotlinBuiltIns.isPrimitiveType(this) && !isBoolean()
|
||||||
fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this)
|
fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this)
|
||||||
|
fun KotlinType.isThrowable(): Boolean = isConstructedFromClassWithGivenFqName(KotlinBuiltIns.FQ_NAMES.throwable) && !isMarkedNullable
|
||||||
|
|
||||||
|
fun KotlinType.isConstructedFromClassWithGivenFqName(fqName: FqName) =
|
||||||
|
(constructor.declarationDescriptor as? ClassDescriptor)?.fqNameUnsafe == fqName.toUnsafe()
|
||||||
|
|
||||||
fun KotlinType.isTypeParameter(): Boolean = TypeUtils.isTypeParameter(this)
|
fun KotlinType.isTypeParameter(): Boolean = TypeUtils.isTypeParameter(this)
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
import org.jetbrains.kotlin.types.typeUtil.*
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
|
||||||
import org.jetbrains.kotlin.util.MemberKindCheck.Member
|
import org.jetbrains.kotlin.util.MemberKindCheck.Member
|
||||||
import org.jetbrains.kotlin.util.MemberKindCheck.MemberOrExtension
|
import org.jetbrains.kotlin.util.MemberKindCheck.MemberOrExtension
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.ASSIGNMENT_OPERATIONS
|
import org.jetbrains.kotlin.util.OperatorNameConventions.ASSIGNMENT_OPERATIONS
|
||||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.BINARY_OPERATION_NAMES
|
|||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.COMPARE_TO
|
import org.jetbrains.kotlin.util.OperatorNameConventions.COMPARE_TO
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.COMPONENT_REGEX
|
import org.jetbrains.kotlin.util.OperatorNameConventions.COMPONENT_REGEX
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.CONTAINS
|
import org.jetbrains.kotlin.util.OperatorNameConventions.CONTAINS
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.COROUTINE_HANDLE_RESULT
|
import org.jetbrains.kotlin.util.OperatorNameConventions.COROUTINE_HANDLE_RESULT
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.DEC
|
import org.jetbrains.kotlin.util.OperatorNameConventions.DEC
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
|
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
|
||||||
@@ -188,19 +190,29 @@ object OperatorChecks {
|
|||||||
},
|
},
|
||||||
Checks(ASSIGNMENT_OPERATIONS, MemberOrExtension, ReturnsUnit, SingleValueParameter, NoDefaultAndVarargsCheck),
|
Checks(ASSIGNMENT_OPERATIONS, MemberOrExtension, ReturnsUnit, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||||
Checks(COMPONENT_REGEX, MemberOrExtension, NoValueParameters),
|
Checks(COMPONENT_REGEX, MemberOrExtension, NoValueParameters),
|
||||||
Checks(
|
Checks(COROUTINE_HANDLE_RESULT, Member, ValueParameterCountCheck.Equals(2), ReturnsUnit, NoDefaultAndVarargsCheck,
|
||||||
COROUTINE_HANDLE_RESULT, Member, ValueParameterCountCheck.Equals(2), ReturnsUnit,
|
NoTypeParametersCheck) {
|
||||||
NoDefaultAndVarargsCheck, NoTypeParametersCheck) {
|
checkHandleSecondParameter()
|
||||||
val secondParameter = valueParameters[1]
|
},
|
||||||
ensure(
|
Checks(COROUTINE_HANDLE_EXCEPTION, Member, ValueParameterCountCheck.Equals(2), ReturnsUnit, NoDefaultAndVarargsCheck,
|
||||||
secondParameter.type.constructor.declarationDescriptor?.fqNameUnsafe == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.toUnsafe()
|
NoTypeParametersCheck) {
|
||||||
&& secondParameter.type.arguments[0].type.isNothing()
|
checkHandleSecondParameter()
|
||||||
) {
|
?: ensure(valueParameters[0].type.isThrowable()) {
|
||||||
"Second parameter should be Continuation<Nothing>"
|
"First parameter should be 'Throwable'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun FunctionDescriptor.checkHandleSecondParameter(): String? {
|
||||||
|
val secondParameter = valueParameters[1]
|
||||||
|
return ensure(
|
||||||
|
secondParameter.type.isConstructedFromClassWithGivenFqName(DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME)
|
||||||
|
&& secondParameter.type.arguments[0].type.isNothing()
|
||||||
|
) {
|
||||||
|
"Second parameter should be Continuation<Nothing>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun checkOperator(functionDescriptor: FunctionDescriptor): CheckResult {
|
fun checkOperator(functionDescriptor: FunctionDescriptor): CheckResult {
|
||||||
for (check in CHECKS) {
|
for (check in CHECKS) {
|
||||||
if (!check.isApplicable(functionDescriptor)) continue
|
if (!check.isApplicable(functionDescriptor)) continue
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ object OperatorNameConventions {
|
|||||||
@JvmField val PLUS_ASSIGN = Name.identifier("plusAssign")
|
@JvmField val PLUS_ASSIGN = Name.identifier("plusAssign")
|
||||||
@JvmField val MINUS_ASSIGN = Name.identifier("minusAssign")
|
@JvmField val MINUS_ASSIGN = Name.identifier("minusAssign")
|
||||||
@JvmField val COROUTINE_HANDLE_RESULT = Name.identifier("handleResult")
|
@JvmField val COROUTINE_HANDLE_RESULT = Name.identifier("handleResult")
|
||||||
|
@JvmField val COROUTINE_HANDLE_EXCEPTION = Name.identifier("handleException")
|
||||||
|
|
||||||
// If you add new unary, binary or assignment operators, add it to OperatorConventions as well
|
// If you add new unary, binary or assignment operators, add it to OperatorConventions as well
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user