[NI] Resolve lambda against input types from expected type
This is needed to report more precise diagnostics and fix IDE-tests #KT-31059 Fixed
This commit is contained in:
+11
-2
@@ -97,7 +97,15 @@ private fun extraLambdaInfo(
|
|||||||
val newTypeVariableUsed = returnType == typeVariable.defaultType
|
val newTypeVariableUsed = returnType == typeVariable.defaultType
|
||||||
if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable)
|
if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable)
|
||||||
|
|
||||||
return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed })
|
return ResolvedLambdaAtom(
|
||||||
|
argument,
|
||||||
|
isSuspend,
|
||||||
|
receiverType,
|
||||||
|
parameters,
|
||||||
|
returnType,
|
||||||
|
typeVariable.takeIf { newTypeVariableUsed },
|
||||||
|
expectedType
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractLambdaInfoFromFunctionalType(expectedType: UnwrappedType?, argument: LambdaKotlinCallArgument): ResolvedLambdaAtom? {
|
private fun extractLambdaInfoFromFunctionalType(expectedType: UnwrappedType?, argument: LambdaKotlinCallArgument): ResolvedLambdaAtom? {
|
||||||
@@ -114,7 +122,8 @@ private fun extractLambdaInfoFromFunctionalType(expectedType: UnwrappedType?, ar
|
|||||||
receiverType,
|
receiverType,
|
||||||
parameters,
|
parameters,
|
||||||
returnType,
|
returnType,
|
||||||
typeVariableForLambdaReturnType = null
|
typeVariableForLambdaReturnType = null,
|
||||||
|
expectedType = expectedType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-3
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.components
|
package org.jetbrains.kotlin.resolve.calls.components
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.addSubsystemFromArgument
|
import org.jetbrains.kotlin.resolve.calls.inference.addSubsystemFromArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
||||||
@@ -68,15 +71,31 @@ class PostponedArgumentsAnalyzer(
|
|||||||
|
|
||||||
fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type)
|
fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(type)
|
||||||
|
|
||||||
val receiver = lambda.receiver?.let(::substitute)
|
fun expectedOrActualType(expected: UnwrappedType?, actual: UnwrappedType?): UnwrappedType? {
|
||||||
val parameters = lambda.parameters.map(::substitute)
|
val expectedSubstituted = expected?.let(::substitute)
|
||||||
|
return if (expectedSubstituted != null && c.canBeProper(expectedSubstituted)) expectedSubstituted else actual?.let(::substitute)
|
||||||
|
}
|
||||||
|
|
||||||
|
val builtIns = c.getBuilder().builtIns
|
||||||
|
|
||||||
|
// Expected type has a higher priority against which lambda should be analyzed
|
||||||
|
// Mostly, this is needed to report more specific diagnostics on lambda parameters
|
||||||
|
val receiver = expectedOrActualType(lambda.expectedType.receiver(), lambda.receiver)
|
||||||
|
|
||||||
|
val expectedParameters = lambda.expectedType.valueParameters()
|
||||||
|
|
||||||
|
val parameters =
|
||||||
|
expectedParameters?.mapIndexed { index, expected ->
|
||||||
|
expectedOrActualType(expected, lambda.parameters.getOrNull(index)) ?: builtIns.nothingType
|
||||||
|
} ?: lambda.parameters.map(::substitute)
|
||||||
|
|
||||||
val rawReturnType = lambda.returnType
|
val rawReturnType = lambda.returnType
|
||||||
|
|
||||||
val expectedTypeForReturnArguments = when {
|
val expectedTypeForReturnArguments = when {
|
||||||
c.canBeProper(rawReturnType) -> substitute(rawReturnType)
|
c.canBeProper(rawReturnType) -> substitute(rawReturnType)
|
||||||
|
|
||||||
// For Unit-coercion
|
// For Unit-coercion
|
||||||
c.hasUpperOrEqualUnitConstraint(rawReturnType) -> lambda.returnType.builtIns.unitType
|
c.hasUpperOrEqualUnitConstraint(rawReturnType) -> builtIns.unitType
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -121,4 +140,16 @@ class PostponedArgumentsAnalyzer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun UnwrappedType?.receiver(): UnwrappedType? {
|
||||||
|
return forFunctionalType { getReceiverTypeFromFunctionType()?.unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun UnwrappedType?.valueParameters(): List<UnwrappedType>? {
|
||||||
|
return forFunctionalType { getValueParameterTypesFromFunctionType().map { it.type.unwrap() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> UnwrappedType?.forFunctionalType(f: UnwrappedType.() -> T?): T? {
|
||||||
|
return if (this?.isBuiltinFunctionalType == true) f(this) else null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,8 @@ class ResolvedLambdaAtom(
|
|||||||
val receiver: UnwrappedType?,
|
val receiver: UnwrappedType?,
|
||||||
val parameters: List<UnwrappedType>,
|
val parameters: List<UnwrappedType>,
|
||||||
val returnType: UnwrappedType,
|
val returnType: UnwrappedType,
|
||||||
val typeVariableForLambdaReturnType: TypeVariableForLambdaReturnType?
|
val typeVariableForLambdaReturnType: TypeVariableForLambdaReturnType?,
|
||||||
|
val expectedType: UnwrappedType?
|
||||||
) : PostponedResolvedAtom() {
|
) : PostponedResolvedAtom() {
|
||||||
lateinit var resultArguments: List<KotlinCallArgument>
|
lateinit var resultArguments: List<KotlinCallArgument>
|
||||||
private set
|
private set
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ fun text() {
|
|||||||
bar2 {<!TYPE_MISMATCH!><!>}
|
bar2 {<!TYPE_MISMATCH!><!>}
|
||||||
bar2 {1}
|
bar2 {1}
|
||||||
bar2 {<!UNRESOLVED_REFERENCE!>it<!>}
|
bar2 {<!UNRESOLVED_REFERENCE!>it<!>}
|
||||||
bar2 <!NI;TYPE_MISMATCH!>{<!OI;CANNOT_INFER_PARAMETER_TYPE, OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!>it<!> -> <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>it<!>}<!>
|
bar2 <!NI;TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE, EXPECTED_PARAMETERS_NUMBER_MISMATCH!>it<!> -> <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>it<!>}<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bar(<!UNUSED_PARAMETER!>f<!> : (Int, Int) -> Int) {}
|
fun bar(<!UNUSED_PARAMETER!>f<!> : (Int, Int) -> Int) {}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ fun main(args : Array<String>) {
|
|||||||
foo2()({})
|
foo2()({})
|
||||||
foo2()<!TOO_MANY_ARGUMENTS!>{}<!>
|
foo2()<!TOO_MANY_ARGUMENTS!>{}<!>
|
||||||
(foo2()){}
|
(foo2()){}
|
||||||
(foo2())<!NI;TYPE_MISMATCH!>{<!OI;CANNOT_INFER_PARAMETER_TYPE, OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> }<!>
|
(foo2())<!NI;TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE, EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> }<!>
|
||||||
foo2()(<!NI;TYPE_MISMATCH!>{<!OI;CANNOT_INFER_PARAMETER_TYPE, OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> }<!>)
|
foo2()(<!NI;TYPE_MISMATCH!>{<!CANNOT_INFER_PARAMETER_TYPE, EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> }<!>)
|
||||||
|
|
||||||
val a = fooT1(1)()
|
val a = fooT1(1)()
|
||||||
checkSubtype<Int>(a)
|
checkSubtype<Int>(a)
|
||||||
|
|||||||
+2
-2
@@ -19,9 +19,9 @@ fun test1(a: (Int) -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test2(a: (Int) -> Unit) {
|
fun test2(a: (Int) -> Unit) {
|
||||||
test2(<!TYPE_MISMATCH!>fun (<!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>x: String<!>) {}<!>)
|
test2(<!TYPE_MISMATCH!>fun (<!EXPECTED_PARAMETER_TYPE_MISMATCH!>x: String<!>) {}<!>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test3(a: (Int, String) -> Unit) {
|
fun test3(a: (Int, String) -> Unit) {
|
||||||
test3(<!TYPE_MISMATCH!>fun <!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!>(<!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>x: String<!>)<!> {}<!>)
|
test3(<!TYPE_MISMATCH!>fun <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!>(<!EXPECTED_PARAMETER_TYPE_MISMATCH!>x: String<!>)<!> {}<!>)
|
||||||
}
|
}
|
||||||
Vendored
+1
-1
@@ -16,6 +16,6 @@ fun test(s: Sub) {
|
|||||||
t: Trait -> s
|
t: Trait -> s
|
||||||
}
|
}
|
||||||
|
|
||||||
foo(<!TYPE_MISMATCH!>fun(<!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>t: Sub<!>) = s<!>)
|
foo(<!TYPE_MISMATCH!>fun(<!EXPECTED_PARAMETER_TYPE_MISMATCH!>t: Sub<!>) = s<!>)
|
||||||
foo(<!TYPE_MISMATCH!>fun(t): Super = s<!>)
|
foo(<!TYPE_MISMATCH!>fun(t): Super = s<!>)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -10,23 +10,23 @@ fun test1() {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
foo0 <!NI;TYPE_MISMATCH!>{
|
foo0 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!>-> ""
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!>-> ""
|
||||||
}<!>
|
}<!>
|
||||||
foo0 <!NI;TYPE_MISMATCH!>{
|
foo0 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!OI;CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>x<!>, <!OI;CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>y<!><!> -> ""
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>x<!>, <!NI;CANNOT_INFER_PARAMETER_TYPE, OI;CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>y<!><!> -> ""
|
||||||
}<!>
|
}<!>
|
||||||
|
|
||||||
foo1 {
|
foo1 {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
foo1 <!NI;TYPE_MISMATCH!>{
|
foo1 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!> -> ""
|
<!EXPECTED_PARAMETER_TYPE_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!> -> ""
|
||||||
}<!>
|
}<!>
|
||||||
foo1 <!NI;TYPE_MISMATCH!>{
|
foo1 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>x<!>, <!OI;CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>y<!><!> -> ""
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>x<!>, <!CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>y<!><!> -> ""
|
||||||
}<!>
|
}<!>
|
||||||
foo1 <!NI;TYPE_MISMATCH!>{
|
foo1 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!>-> <!CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!>-> <!CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
|
||||||
}<!>
|
}<!>
|
||||||
|
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ fun test1() {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
foo2 <!NI;TYPE_MISMATCH!>{
|
foo2 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH, OI;EXPECTED_PARAMETER_TYPE_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!> -> ""
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH, EXPECTED_PARAMETER_TYPE_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s<!>: String<!> -> ""
|
||||||
}<!>
|
}<!>
|
||||||
foo2 <!NI;TYPE_MISMATCH!>{
|
foo2 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> ""
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH, UNUSED_ANONYMOUS_PARAMETER!>x<!> -> ""
|
||||||
}<!>
|
}<!>
|
||||||
foo2 <!NI;TYPE_MISMATCH!>{
|
foo2 <!NI;TYPE_MISMATCH!>{
|
||||||
<!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!>-> <!CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
|
<!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!>-> <!CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH, NI;CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!>
|
||||||
}<!>
|
}<!>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ fun bar() {
|
|||||||
b checkType { _<String>() }
|
b checkType { _<String>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
foo <!NI;TYPE_MISMATCH!>{ <!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>(a, b): B<!> ->
|
foo <!NI;TYPE_MISMATCH!>{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>(a, b): B<!> ->
|
||||||
a checkType { _<Double>() }
|
a checkType { _<Double>() }
|
||||||
b checkType { _<Short>() }
|
b checkType { _<Short>() }
|
||||||
}<!>
|
}<!>
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ fun bar() {
|
|||||||
b checkType { _<String>() }
|
b checkType { _<String>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
foo <!NI;TYPE_MISMATCH!>{ <!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>(_, b): B<!> ->
|
foo <!NI;TYPE_MISMATCH!>{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>(_, b): B<!> ->
|
||||||
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
|
<!UNRESOLVED_REFERENCE!>_<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>hashCode<!>()
|
||||||
b checkType { _<Short>() }
|
b checkType { _<Short>() }
|
||||||
}<!>
|
}<!>
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ public interface J2 extends J {
|
|||||||
|
|
||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
fun main() {
|
fun main() {
|
||||||
J <!NI;TYPE_MISMATCH!>{ <!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!> -> s}<!> // should be prohibited, because SAM value parameter has nullable type
|
J <!NI;TYPE_MISMATCH!>{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!> -> s}<!> // should be prohibited, because SAM value parameter has nullable type
|
||||||
J { "" + it<!UNSAFE_CALL!>.<!>length }
|
J { "" + it<!UNSAFE_CALL!>.<!>length }
|
||||||
J { <!NI;NULL_FOR_NONNULL_TYPE, NI;NULL_FOR_NONNULL_TYPE, NULL_FOR_NONNULL_TYPE!>null<!> }
|
J { <!NI;NULL_FOR_NONNULL_TYPE, NI;NULL_FOR_NONNULL_TYPE, NULL_FOR_NONNULL_TYPE!>null<!> }
|
||||||
J { <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, TYPE_MISMATCH!>it?.length?.toString()<!> }
|
J { <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, TYPE_MISMATCH!>it?.length?.toString()<!> }
|
||||||
|
|
||||||
J2 <!NI;TYPE_MISMATCH!>{ <!OI;EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!> -> s}<!>
|
J2 <!NI;TYPE_MISMATCH!>{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>s: String<!> -> s}<!>
|
||||||
J2 { "" + it<!UNSAFE_CALL!>.<!>length }
|
J2 { "" + it<!UNSAFE_CALL!>.<!>length }
|
||||||
J2 { <!NI;NULL_FOR_NONNULL_TYPE, NI;NULL_FOR_NONNULL_TYPE, NULL_FOR_NONNULL_TYPE!>null<!> }
|
J2 { <!NI;NULL_FOR_NONNULL_TYPE, NI;NULL_FOR_NONNULL_TYPE, NULL_FOR_NONNULL_TYPE!>null<!> }
|
||||||
J2 { <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, TYPE_MISMATCH!>it?.length?.toString()<!> }
|
J2 { <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, TYPE_MISMATCH!>it?.length?.toString()<!> }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ val test3: (String) -> Boolean =
|
|||||||
|
|
||||||
val test4: (String) -> Boolean =
|
val test4: (String) -> Boolean =
|
||||||
when {
|
when {
|
||||||
true -> <!NI;TYPE_MISMATCH!>{ <!OI;EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s1<!>, <!OI;CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>s2<!><!> -> true }<!>
|
true -> <!NI;TYPE_MISMATCH!>{ <!EXPECTED_PARAMETERS_NUMBER_MISMATCH!><!UNUSED_ANONYMOUS_PARAMETER!>s1<!>, <!CANNOT_INFER_PARAMETER_TYPE, UNUSED_ANONYMOUS_PARAMETER!>s2<!><!> -> true }<!>
|
||||||
else -> null!!
|
else -> null!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user