From 47e680518661eec004ab3390d4f5c3480ee75e8d Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Sun, 31 May 2020 23:50:19 +0300 Subject: [PATCH] Add compatibility warning for reference adaptation --- ...irOldFrontendDiagnosticsTestGenerated.java | 5 ++ .../DiagnosticReporterByTrackingStrategy.kt | 4 + .../components/CallableReferenceResolution.kt | 23 +++++- .../components/CallableReferenceResolver.kt | 8 +- .../calls/model/KotlinCallDiagnostics.kt | 6 ++ .../referenceAdaptationCompatibility.fir.kt | 57 +++++++++++++++ .../referenceAdaptationCompatibility.kt | 57 +++++++++++++++ .../referenceAdaptationCompatibility.txt | 73 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 5 ++ .../DiagnosticsUsingJavacTestGenerated.java | 5 ++ 10 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.fir.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.txt diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index f333ffe4d97..d6cf9f90587 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -1934,6 +1934,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/callableReference/propertyOfNestedGenericClass.kt"); } + @TestMetadata("referenceAdaptationCompatibility.kt") + public void testReferenceAdaptationCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt"); + } + @TestMetadata("rewriteAtSliceOnGetOperator.kt") public void testRewriteAtSliceOnGetOperator() throws Exception { runTest("compiler/testData/diagnostics/tests/callableReference/rewriteAtSliceOnGetOperator.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index 62b63747dea..b0069563864 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -218,6 +218,10 @@ class DiagnosticReporterByTrackingStrategy( trace.report(CANNOT_INFER_PARAMETER_TYPE.on(parameter)) } } + + CompatibilityWarningOnArgument::class.java -> { + trace.report(COMPATIBILITY_WARNING.on(callArgument.psiCallArgument.valueArgument.asElement())) + } } } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt index f7a6430aead..7f33ce68ebe 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolution.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstit import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.DISPATCH_RECEIVER @@ -60,12 +61,17 @@ class CallableReferenceCandidate( val explicitReceiverKind: ExplicitReceiverKind, val reflectionCandidateType: UnwrappedType, val callableReferenceAdaptation: CallableReferenceAdaptation?, - val diagnostics: List + initialDiagnostics: List ) : Candidate { + private val mutableDiagnostics = initialDiagnostics.toMutableList() + val diagnostics: List = mutableDiagnostics + override val resultingApplicability = getResultApplicability(diagnostics) override fun addCompatibilityWarning(other: Candidate) { - // TODO: now only arguments can be converted, so CR candidates shouldn't be affected + if (this !== other) { + mutableDiagnostics.add(CompatibilityWarning()) + } } override val isSuccessful get() = resultingApplicability.isSuccess @@ -207,6 +213,10 @@ class CallableReferencesCandidateFactory( callComponents.builtIns ) + if (needCompatibilityWarning(callableReferenceAdaptation)) { + diagnostics.add(LowerPriorityToPreserveCompatibility) + } + if (callableReferenceAdaptation != null && callableReferenceAdaptation.defaults != 0 && !callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType) @@ -251,6 +261,15 @@ class CallableReferencesCandidateFactory( ) } + private fun needCompatibilityWarning(callableReferenceAdaptation: CallableReferenceAdaptation?): Boolean { + if (callableReferenceAdaptation == null) return false + + return callableReferenceAdaptation.defaults != 0 || + callableReferenceAdaptation.suspendConversionStrategy != SuspendConversionStrategy.NO_CONVERSION || + callableReferenceAdaptation.coercionStrategy != CoercionStrategy.NO_COERCION || + callableReferenceAdaptation.mappedArguments.values.any { it is ResolvedCallArgument.VarargArgument } + } + private enum class VarargMappingState { UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt index ecf44976b81..78fae362809 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CallableReferenceResolver.kt @@ -106,7 +106,13 @@ class CallableReferenceResolver( ) } diagnosticsHolder.addDiagnosticIfNotNull(diagnostic) - chosenCandidate.diagnostics.forEach { diagnosticsHolder.addDiagnostic(it) } + chosenCandidate.diagnostics.forEach { + val transformedDiagnostic = when (it) { + is CompatibilityWarning -> CompatibilityWarningOnArgument(argument) + else -> it + } + diagnosticsHolder.addDiagnostic(transformedDiagnostic) + } chosenCandidate.freshSubstitutor = toFreshSubstitutor } else { if (candidates.isEmpty()) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt index 7839e537fd4..b1efe3ebc94 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt @@ -254,4 +254,10 @@ class CompatibilityWarning : KotlinCallDiagnostic(RESOLVED) { override fun report(reporter: DiagnosticReporter) { reporter.onCall(this) } +} + +class CompatibilityWarningOnArgument(val argument: CallableReferenceKotlinCallArgument) : KotlinCallDiagnostic(RESOLVED) { + override fun report(reporter: DiagnosticReporter) { + reporter.onCallArgument(argument, this) + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.fir.kt b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.fir.kt new file mode 100644 index 00000000000..5d649d045aa --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.fir.kt @@ -0,0 +1,57 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +object Test1 { + fun foo(f: () -> T): T = f() + fun bar(): Int = 0 + + object Scope { + fun bar(x: Int = 0): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} + +object Test2 { + fun foo(f: () -> Unit) {} + fun bar() {} + + object Scope { + fun bar(): String = "" + + fun test() { + foo(::bar) + } + } +} + +object Test3 { + fun foo(f: (Int, Int) -> T): T = TODO() + fun bar(x: Int, y: Int): Int = 0 + + object Scope { + fun bar(vararg ints: Int): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} + +object Test4 { + fun foo(f: (Array) -> T): T = TODO() + fun bar(g: Array): Int = 0 + + object Scope { + // Works before 1.4 + fun bar(vararg g: String): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt new file mode 100644 index 00000000000..53db8897f9d --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt @@ -0,0 +1,57 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +object Test1 { + fun foo(f: () -> T): T = f() + fun bar(): Int = 0 + + object Scope { + fun bar(x: Int = 0): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} + +object Test2 { + fun foo(f: () -> Unit) {} + fun bar() {} + + object Scope { + fun bar(): String = "" + + fun test() { + foo(::bar) + } + } +} + +object Test3 { + fun foo(f: (Int, Int) -> T): T = TODO() + fun bar(x: Int, y: Int): Int = 0 + + object Scope { + fun bar(vararg ints: Int): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} + +object Test4 { + fun foo(f: (Array) -> T): T = TODO() + fun bar(g: Array): Int = 0 + + object Scope { + // Works before 1.4 + fun bar(vararg g: String): String = "" + + fun test() { + val result = foo(::bar) + result + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.txt b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.txt new file mode 100644 index 00000000000..c5c27282cc4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.txt @@ -0,0 +1,73 @@ +package + +public object Test1 { + private constructor Test1() + public final fun bar(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: () -> T): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Scope { + private constructor Scope() + public final fun bar(/*0*/ x: kotlin.Int = ...): kotlin.String + 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 final fun test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public object Test2 { + private constructor Test2() + public final fun bar(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Scope { + private constructor Scope() + public final fun bar(): kotlin.String + 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 final fun test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public object Test3 { + private constructor Test3() + public final fun bar(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: (kotlin.Int, kotlin.Int) -> T): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Scope { + private constructor Scope() + public final fun bar(/*0*/ vararg ints: kotlin.Int /*kotlin.IntArray*/): kotlin.String + 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 final fun test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public object Test4 { + private constructor Test4() + public final fun bar(/*0*/ g: kotlin.Array): kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: (kotlin.Array) -> T): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Scope { + private constructor Scope() + public final fun bar(/*0*/ vararg g: kotlin.String /*kotlin.Array*/): kotlin.String + 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 final fun test(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 40c660d9eac..f9bd7827178 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -1941,6 +1941,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/callableReference/propertyOfNestedGenericClass.kt"); } + @TestMetadata("referenceAdaptationCompatibility.kt") + public void testReferenceAdaptationCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt"); + } + @TestMetadata("rewriteAtSliceOnGetOperator.kt") public void testRewriteAtSliceOnGetOperator() throws Exception { runTest("compiler/testData/diagnostics/tests/callableReference/rewriteAtSliceOnGetOperator.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 9dbc2711f0d..069e6fb4eb5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -1936,6 +1936,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/callableReference/propertyOfNestedGenericClass.kt"); } + @TestMetadata("referenceAdaptationCompatibility.kt") + public void testReferenceAdaptationCompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/callableReference/referenceAdaptationCompatibility.kt"); + } + @TestMetadata("rewriteAtSliceOnGetOperator.kt") public void testRewriteAtSliceOnGetOperator() throws Exception { runTest("compiler/testData/diagnostics/tests/callableReference/rewriteAtSliceOnGetOperator.kt");