[NI] Make behaviour of anonymous functions consistent with lambdas

Fix completion of anonymous functions with expression body without expected type.
Premature completion led to losing type info from outer calls.
Also report type mismatches on empty lambda expressions.

KT-34729 In progress
This commit is contained in:
Pavel Kirpichenkov
2020-01-20 13:47:43 +03:00
parent 4554f91aff
commit 3a98c84105
14 changed files with 185 additions and 13 deletions
@@ -8469,6 +8469,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt");
}
@TestMetadata("kt34729.kt")
public void testKt34729() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt");
}
@TestMetadata("kt5508.kt")
public void testKt5508() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt");
@@ -10537,6 +10542,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("anonymousFunction.kt")
public void testAnonymousFunction() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt");
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");
@@ -273,6 +273,7 @@ class DiagnosticReporterByTrackingStrategy(
is ArgumentConstraintPosition -> position.argument
is ReceiverConstraintPosition -> position.argument
is LHSArgumentConstraintPosition -> position.argument
is LambdaArgumentConstraintPosition -> position.lambda.atom
else -> null
}
argument?.let {
@@ -56,6 +56,7 @@ import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.kotlin.types.TypeUtils.*;
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingServices.getNewInferenceLambdaInfo;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*;
public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@@ -945,17 +946,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
replaceJumpOutPossible(true);
}
@Nullable
private static KotlinResolutionCallbacksImpl.LambdaInfo getNewInferenceLambdaInfo(
@NotNull ExpressionTypingContext context,
@NotNull KtElement function
) {
if (function instanceof KtFunction) {
return context.trace.get(BindingContext.NEW_INFERENCE_LAMBDA_INFO, (KtFunction) function);
}
return null;
}
@NotNull
private static KotlinType getFunctionExpectedReturnType(
@NotNull FunctionDescriptor descriptor,
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl;
import org.jetbrains.kotlin.resolve.calls.tower.LambdaContextInfo;
import org.jetbrains.kotlin.resolve.scopes.*;
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType;
@@ -224,7 +225,11 @@ public class ExpressionTypingServices {
trace, functionInnerScope, dataFlowInfo, NO_EXPECTED_TYPE, getLanguageVersionSettings(),
expressionTypingComponents.dataFlowValueFactory
);
KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo = getNewInferenceLambdaInfo(context, function);
context = updateContextFromNILambdaInfo(lambdaInfo, context);
KotlinTypeInfo typeInfo = expressionTypingFacade.getTypeInfo(bodyExpression, context, function.hasBlockBody());
updateLambdaContextInfoForAnonymousFunction(lambdaInfo, typeInfo, context);
KotlinType type = typeInfo.getType();
if (type != null) {
@@ -235,6 +240,43 @@ public class ExpressionTypingServices {
}
}
private static void updateLambdaContextInfoForAnonymousFunction(
@Nullable KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo,
@NotNull KotlinTypeInfo bodyExpressionTypeInfo,
@NotNull ExpressionTypingContext context
) {
if (lambdaInfo == null) return;
LambdaContextInfo contextInfo = lambdaInfo.getLastExpressionInfo();
contextInfo.setTypeInfo(bodyExpressionTypeInfo);
contextInfo.setDataFlowInfoAfter(null);
contextInfo.setLexicalScope(context.scope);
contextInfo.setTrace(context.trace);
}
private static ExpressionTypingContext updateContextFromNILambdaInfo(
@Nullable KotlinResolutionCallbacksImpl.LambdaInfo lambdaInfo,
@NotNull ExpressionTypingContext context
) {
if (lambdaInfo != null) {
context = context
.replaceContextDependency(lambdaInfo.getContextDependency())
.replaceExpectedType(lambdaInfo.getExpectedType());
}
return context;
}
@Nullable
public static KotlinResolutionCallbacksImpl.LambdaInfo getNewInferenceLambdaInfo(
@NotNull ExpressionTypingContext context,
@NotNull KtElement function
) {
if (function instanceof KtFunction) {
return context.trace.get(BindingContext.NEW_INFERENCE_LAMBDA_INFO, (KtFunction) function);
}
return null;
}
/**
* Visits block statements propagating data flow information from the first to the last.
* Determines block returned type and data flow information at the end of the block AND
@@ -139,7 +139,6 @@ class PostponedArgumentsAnalyzer(
if (returnArguments.isEmpty()) {
val unitType = lambda.returnType.builtIns.unitType
val lambdaReturnType = lambda.returnType.let(::substitute)
c.getBuilder().addSubtypeConstraint(lambdaReturnType, unitType, LambdaArgumentConstraintPosition(lambda))
c.getBuilder().addSubtypeConstraint(unitType, lambdaReturnType, LambdaArgumentConstraintPosition(lambda))
}
@@ -12,7 +12,7 @@ fun test(a: (Int) -> Int) {
}
fun test2(a: () -> List<Int>) {
test2(fun () = <!NI;NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>listOf<!>())
test2(fun () = <!OI;TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>listOf<!>())
}
val a: (Int) -> Unit = fun(x) { checkSubtype<Int>(x) }
@@ -0,0 +1,19 @@
// !WITH_NEW_INFERENCE
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +NewInference
interface ILength {
val length: Int
}
class Impl(override val length: Int) : ILength
fun <T> foo(a: (Int) -> T) = 0
fun <T : ILength> bar(a: (Int) -> T) {
a(42).length
}
fun test() {
foo<String> { }
bar<Impl> { }
}
+19
View File
@@ -0,0 +1,19 @@
// !WITH_NEW_INFERENCE
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +NewInference
interface ILength {
val length: Int
}
class Impl(override val length: Int) : ILength
fun <T> foo(a: (Int) -> T) = 0
fun <T : ILength> bar(a: (Int) -> T) {
a(42).length
}
fun test() {
foo<String> <!NI;TYPE_MISMATCH!>{ <!OI;TYPE_MISMATCH!><!>}<!>
bar<Impl> <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>{ <!OI;TYPE_MISMATCH!><!>}<!>
}
@@ -0,0 +1,20 @@
package
public fun </*0*/ T : ILength> bar(/*0*/ a: (kotlin.Int) -> T): kotlin.Unit
public fun </*0*/ T> foo(/*0*/ a: (kotlin.Int) -> T): kotlin.Int
public fun test(): kotlin.Unit
public interface ILength {
public abstract val length: kotlin.Int
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 Impl : ILength {
public constructor Impl(/*0*/ length: kotlin.Int)
public open override /*1*/ val length: kotlin.Int
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
}
@@ -0,0 +1,22 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun take(fn: () -> List<String>) {}
fun <L> inferFromLambda(fn: () -> L): L = TODO()
fun <T> materialize(): T = TODO()
fun <I> id(arg: I) = arg
fun testFunctions() {
take { materialize() }
take(fun() = materialize())
take(fun(): List<String> = materialize())
take(fun(): List<String> {
return materialize()
})
}
fun testNestedCalls() {
id<String>(inferFromLambda { materialize() })
id<String>(inferFromLambda(fun() = materialize()))
}
@@ -0,0 +1,22 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun take(fn: () -> List<String>) {}
fun <L> inferFromLambda(fn: () -> L): L = TODO()
fun <T> materialize(): T = TODO()
fun <I> id(arg: I) = arg
fun testFunctions() {
take { materialize() }
take(fun() = materialize())
take(fun(): List<String> = materialize())
take(fun(): List<String> {
return materialize()
})
}
fun testNestedCalls() {
id<String>(inferFromLambda { materialize() })
id<String>(inferFromLambda(fun() = materialize()))
}
@@ -0,0 +1,8 @@
package
public fun </*0*/ I> id(/*0*/ arg: I): I
public fun </*0*/ L> inferFromLambda(/*0*/ fn: () -> L): L
public fun </*0*/ T> materialize(): T
public fun take(/*0*/ fn: () -> kotlin.collections.List<kotlin.String>): kotlin.Unit
public fun testFunctions(): kotlin.Unit
public fun testNestedCalls(): kotlin.Unit
@@ -8476,6 +8476,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt");
}
@TestMetadata("kt34729.kt")
public void testKt34729() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt");
}
@TestMetadata("kt5508.kt")
public void testKt5508() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt");
@@ -10544,6 +10549,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("anonymousFunction.kt")
public void testAnonymousFunction() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt");
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");
@@ -8471,6 +8471,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/generics/kt30590.kt");
}
@TestMetadata("kt34729.kt")
public void testKt34729() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt34729.kt");
}
@TestMetadata("kt5508.kt")
public void testKt5508() throws Exception {
runTest("compiler/testData/diagnostics/tests/generics/kt5508.kt");
@@ -10539,6 +10544,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/completion"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@TestMetadata("anonymousFunction.kt")
public void testAnonymousFunction() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/anonymousFunction.kt");
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");