Don't apply Unit-conversion for expressions that return Nothing type

This commit is contained in:
Mikhail Zarechenskiy
2020-06-05 15:44:53 +03:00
parent 3634cbe3cb
commit 22b558110d
4 changed files with 25 additions and 11 deletions
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.isDynamic import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
object UnitTypeConversions : ParameterTypeConversion { object UnitTypeConversions : ParameterTypeConversion {
@@ -26,8 +27,8 @@ object UnitTypeConversions : ParameterTypeConversion {
if (argument !is SimpleKotlinCallArgument) return true if (argument !is SimpleKotlinCallArgument) return true
val receiver = argument.receiver val receiver = argument.receiver
if (receiver.receiverValue.type.hasUnitOrDynamicReturnType()) return true if (receiver.receiverValue.type.hasUnitOrSubtypeReturnType()) return true
if (receiver.typesFromSmartCasts.any { it.hasUnitOrDynamicReturnType() }) return true if (receiver.typesFromSmartCasts.any { it.hasUnitOrSubtypeReturnType() }) return true
if ( if (
!expectedParameterType.isBuiltinFunctionalType || !expectedParameterType.isBuiltinFunctionalType ||
@@ -37,10 +38,10 @@ object UnitTypeConversions : ParameterTypeConversion {
return false return false
} }
private fun KotlinType.hasUnitOrDynamicReturnType(): Boolean = private fun KotlinType.hasUnitOrSubtypeReturnType(): Boolean =
isFunctionOrKFunctionTypeWithAnySuspendability && arguments.last().type.isUnitOrDynamic() isFunctionOrKFunctionTypeWithAnySuspendability && arguments.last().type.isUnitOrSubtype()
private fun KotlinType.isUnitOrDynamic(): Boolean = isUnit() || isDynamic() private fun KotlinType.isUnitOrSubtype(): Boolean = isUnit() || isDynamic() || isNothing()
override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean = override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean =
argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionType argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionType
@@ -51,7 +52,7 @@ object UnitTypeConversions : ParameterTypeConversion {
var isFunctionTypeOrSubtype = false var isFunctionTypeOrSubtype = false
val hasReturnTypeInSubtypes = argument.receiver.stableType.isFunctionTypeOrSubtype { val hasReturnTypeInSubtypes = argument.receiver.stableType.isFunctionTypeOrSubtype {
isFunctionTypeOrSubtype = true isFunctionTypeOrSubtype = true
it.getReturnTypeFromFunctionType().isUnitOrDynamic() it.getReturnTypeFromFunctionType().isUnitOrSubtype()
} }
if (!isFunctionTypeOrSubtype) return false if (!isFunctionTypeOrSubtype) return false
@@ -1,4 +1,4 @@
// !LANGUAGE: -SuspendConversion // !LANGUAGE: -UnitConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
fun foo(f: () -> Unit) {} fun foo(f: () -> Unit) {}
@@ -7,9 +7,15 @@ fun bar(): Int = 0
abstract class SubInt : () -> Int abstract class SubInt : () -> Int
fun test(f: () -> String, s: SubInt) { fun <T> T.freeze(): T = TODO()
fun test(f: () -> String, g: () -> Nothing, h: () -> Nothing?, s: SubInt) {
foo { "lambda" } foo { "lambda" }
foo(::bar) foo(::bar)
foo({ TODO() }.freeze())
foo(g)
<!INAPPLICABLE_CANDIDATE!>foo<!>(h)
<!INAPPLICABLE_CANDIDATE!>foo<!>(f) <!INAPPLICABLE_CANDIDATE!>foo<!>(f)
<!INAPPLICABLE_CANDIDATE!>foo<!>(s) <!INAPPLICABLE_CANDIDATE!>foo<!>(s)
@@ -1,4 +1,4 @@
// !LANGUAGE: -SuspendConversion // !LANGUAGE: -UnitConversion
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
fun foo(f: () -> Unit) {} fun foo(f: () -> Unit) {}
@@ -7,9 +7,15 @@ fun bar(): Int = 0
abstract class SubInt : () -> Int abstract class SubInt : () -> Int
fun test(f: () -> String, s: SubInt) { fun <T> T.freeze(): T = TODO()
fun test(f: () -> String, g: () -> Nothing, h: () -> Nothing?, s: SubInt) {
foo { "lambda" } foo { "lambda" }
foo(::bar) foo(::bar)
foo({ TODO() }.freeze())
foo(g)
foo(<!UNSUPPORTED_FEATURE!>h<!>)
foo(<!UNSUPPORTED_FEATURE!>f<!>) foo(<!UNSUPPORTED_FEATURE!>f<!>)
foo(<!UNSUPPORTED_FEATURE!>s<!>) foo(<!UNSUPPORTED_FEATURE!>s<!>)
@@ -2,7 +2,8 @@ package
public fun bar(): kotlin.Int public fun bar(): kotlin.Int
public fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit public fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
public fun test(/*0*/ f: () -> kotlin.String, /*1*/ s: SubInt): kotlin.Unit public fun test(/*0*/ f: () -> kotlin.String, /*1*/ g: () -> kotlin.Nothing, /*2*/ h: () -> kotlin.Nothing?, /*3*/ s: SubInt): kotlin.Unit
public fun </*0*/ T> T.freeze(): T
public abstract class SubInt : () -> kotlin.Int { public abstract class SubInt : () -> kotlin.Int {
public constructor SubInt() public constructor SubInt()