Prohibit adapted reference resolve against reflective types

There are design questions about reflection for adapted references,
 so the current proposal is to prohibit reflection on them and support
 it properly later

 #KT-40406 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2020-07-17 08:45:17 +03:00
parent d8de37f665
commit 0c79de1a98
18 changed files with 258 additions and 5 deletions
@@ -2599,6 +2599,16 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("adaptedReferenceAgainstKCallable.kt")
public void testAdaptedReferenceAgainstKCallable() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstKCallable.kt");
}
@TestMetadata("adaptedReferenceAgainstReflectionType.kt")
public void testAdaptedReferenceAgainstReflectionType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstReflectionType.kt");
}
public void testAllFilesPresentInResolve() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference/resolve"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@@ -2663,6 +2673,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/commonSupertypeFromReturnTypesOfCallableReference.kt");
}
@TestMetadata("compatibilityWarningOnReferenceAgainstReflectiveType.kt")
public void testCompatibilityWarningOnReferenceAgainstReflectiveType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/compatibilityWarningOnReferenceAgainstReflectiveType.kt");
}
@TestMetadata("constructor.kt")
public void testConstructor() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/constructor.kt");
@@ -810,6 +810,8 @@ public interface Errors {
DiagnosticFactory0<PsiElement> CALLABLE_REFERENCE_TO_JAVA_SYNTHETIC_PROPERTY = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE = DiagnosticFactory0.create(ERROR);
// Destructuring-declarations
DiagnosticFactory0<KtDestructuringDeclaration> INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION = DiagnosticFactory0.create(ERROR, DEFAULT);
@@ -990,6 +990,8 @@ public class DefaultErrorMessages {
MAP.put(CALLABLE_REFERENCE_TO_JAVA_SYNTHETIC_PROPERTY, "References to the synthetic extension properties for a Java get/set methods aren't supported fully, please use reference to a method");
MAP.put(ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE, "Adapted callable reference cannot be resolved against reflective types");
//Inline
MAP.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
MAP.put(PRIVATE_CLASS_MEMBER_FROM_INLINE, "Non-private inline function cannot access members of private classes: ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
@@ -236,6 +236,14 @@ class DiagnosticReporterByTrackingStrategy(
)
)
}
AdaptedCallableReferenceIsUsedWithReflection::class.java -> {
trace.report(
ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE.on(
callArgument.psiCallArgument.valueArgument.asElement()
)
)
}
}
}
@@ -228,6 +228,12 @@ class CallableReferencesCandidateFactory(
markCandidateForCompatibilityResolve(diagnostics)
}
if (callableReferenceAdaptation != null && expectedType != null && hasNonTrivialAdaptation(callableReferenceAdaptation)) {
if (!expectedType.isFunctionType && !expectedType.isSuspendFunctionType) { // expectedType has some reflection type
diagnostics.add(AdaptedCallableReferenceIsUsedWithReflection(argument))
}
}
if (callableReferenceAdaptation != null &&
callableReferenceAdaptation.defaults != 0 &&
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType)
@@ -278,11 +284,14 @@ class CallableReferencesCandidateFactory(
if (callableReferenceAdaptation == null) return false
return callableReferenceAdaptation.defaults != 0 ||
return hasNonTrivialAdaptation(callableReferenceAdaptation)
}
private fun hasNonTrivialAdaptation(callableReferenceAdaptation: CallableReferenceAdaptation) =
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
@@ -296,6 +305,11 @@ class CallableReferencesCandidateFactory(
): CallableReferenceAdaptation? {
if (callComponents.languageVersionSettings.apiVersion < ApiVersion.KOTLIN_1_4) return null
if (expectedType == null) return null
// Do not adapt references against KCallable type as it's impossible to map defaults/vararg to absent parameters of KCallable
if (ReflectionTypes.hasKCallableTypeFqName(expectedType)) return null
val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
@@ -263,4 +263,13 @@ class CompatibilityWarningOnArgument(
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(argument, this)
}
}
class AdaptedCallableReferenceIsUsedWithReflection(
val argument: CallableReferenceKotlinCallArgument,
) : KotlinCallDiagnostic(RESOLVED_WITH_ERROR) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(argument, this)
}
}
@@ -0,0 +1,13 @@
// FIR_IDENTICAL
// !LANGUAGE: -AdaptedCallableReferenceAgainstReflectiveType
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.KCallable
fun take(k: KCallable<*>) {}
fun foo(x: Int = 0) {}
fun test() {
take(::foo)
}
@@ -0,0 +1,5 @@
package
public fun foo(/*0*/ x: kotlin.Int = ...): kotlin.Unit
public fun take(/*0*/ k: kotlin.reflect.KCallable<*>): kotlin.Unit
public fun test(): kotlin.Unit
@@ -0,0 +1,27 @@
// !LANGUAGE: -AdaptedCallableReferenceAgainstReflectiveType +DisableCompatibilityModeForNewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.KFunction1
fun foo(x: Int): Unit {} // (1)
fun bar(f: KFunction1<Int, Unit>) {}
fun test() {
bar(::foo) // OK, foo resolved to (1)
}
object Scope {
fun foo(x: Int, y: Int = 0): Int = 0 // (2)
fun test() {
bar(::foo) // Error and foo should be resolved to (2)
}
}
object Local {
fun baz(x: Int, y: Int = 0): Int = 0
fun test() {
bar(::baz)
}
}
@@ -0,0 +1,27 @@
// !LANGUAGE: -AdaptedCallableReferenceAgainstReflectiveType +DisableCompatibilityModeForNewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.KFunction1
fun foo(x: Int): Unit {} // (1)
fun bar(f: KFunction1<Int, Unit>) {}
fun test() {
bar(::foo) // OK, foo resolved to (1)
}
object Scope {
fun foo(x: Int, y: Int = 0): Int = 0 // (2)
fun test() {
bar(<!ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE!>::foo<!>) // Error and foo should be resolved to (2)
}
}
object Local {
fun baz(x: Int, y: Int = 0): Int = 0
fun test() {
bar(<!ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE!>::baz<!>)
}
}
@@ -0,0 +1,23 @@
package
public fun bar(/*0*/ f: kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit>): kotlin.Unit
public fun foo(/*0*/ x: kotlin.Int): kotlin.Unit
public fun test(): kotlin.Unit
public object Local {
private constructor Local()
public final fun baz(/*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 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 Scope {
private constructor Scope()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int = ...): kotlin.Int
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
}
@@ -0,0 +1,27 @@
// !LANGUAGE: -AdaptedCallableReferenceAgainstReflectiveType -DisableCompatibilityModeForNewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.KFunction1
fun foo(x: Int): Unit {} // (1)
fun bar(f: KFunction1<Int, Unit>) {}
fun test() {
bar(::foo)
}
object Scope {
fun foo(x: Int, y: Int = 0): Int = 0 // (2)
fun test() {
bar(::foo)
}
}
object Local {
fun baz(x: Int, y: Int = 0): Int = 0
fun test() {
bar(::baz)
}
}
@@ -0,0 +1,27 @@
// !LANGUAGE: -AdaptedCallableReferenceAgainstReflectiveType -DisableCompatibilityModeForNewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.reflect.KFunction1
fun foo(x: Int): Unit {} // (1)
fun bar(f: KFunction1<Int, Unit>) {}
fun test() {
bar(::foo)
}
object Scope {
fun foo(x: Int, y: Int = 0): Int = 0 // (2)
fun test() {
bar(<!COMPATIBILITY_WARNING!>::foo<!>)
}
}
object Local {
fun baz(x: Int, y: Int = 0): Int = 0
fun test() {
bar(<!ADAPTED_CALLABLE_REFERENCE_AGAINST_REFLECTION_TYPE!>::baz<!>)
}
}
@@ -0,0 +1,23 @@
package
public fun bar(/*0*/ f: kotlin.reflect.KFunction1<kotlin.Int, kotlin.Unit>): kotlin.Unit
public fun foo(/*0*/ x: kotlin.Int): kotlin.Unit
public fun test(): kotlin.Unit
public object Local {
private constructor Local()
public final fun baz(/*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 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 Scope {
private constructor Scope()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int = ...): kotlin.Int
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
}
@@ -58,7 +58,7 @@ FILE fqName:<root> fileName:/funWithDefaultParametersAsKCallableStar.kt
FUN name:testDefaultsOnlyStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUNCTION_REFERENCE 'public final fun defaultsOnly (x: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
fn: FUNCTION_REFERENCE 'public final fun defaultsOnly (x: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction1<kotlin.String, kotlin.Int> origin=null reflectionTarget=<same>
FUN name:testRegularAndDefaultsStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
@@ -66,8 +66,8 @@ FILE fqName:<root> fileName:/funWithDefaultParametersAsKCallableStar.kt
FUN name:testVarargsStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUNCTION_REFERENCE 'public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction0<kotlin.Int> origin=null reflectionTarget=<same>
fn: FUNCTION_REFERENCE 'public final fun varargs (vararg xs: kotlin.String): kotlin.Int declared in <root>' type=kotlin.reflect.KFunction1<kotlin.Array<out kotlin.String>, kotlin.Int> origin=null reflectionTarget=<same>
FUN name:testCtorStar visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun useKCallableStar (fn: kotlin.reflect.KCallable<*>): kotlin.Unit declared in <root>' type=kotlin.Unit origin=null
fn: FUNCTION_REFERENCE 'public constructor <init> (x: kotlin.String) [primary] declared in <root>.C' type=kotlin.reflect.KFunction0<<root>.C> origin=null reflectionTarget=<same>
fn: FUNCTION_REFERENCE 'public constructor <init> (x: kotlin.String) [primary] declared in <root>.C' type=kotlin.reflect.KFunction1<kotlin.String, <root>.C> origin=null reflectionTarget=<same>
@@ -2606,6 +2606,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("adaptedReferenceAgainstKCallable.kt")
public void testAdaptedReferenceAgainstKCallable() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstKCallable.kt");
}
@TestMetadata("adaptedReferenceAgainstReflectionType.kt")
public void testAdaptedReferenceAgainstReflectionType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstReflectionType.kt");
}
public void testAllFilesPresentInResolve() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference/resolve"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@@ -2670,6 +2680,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/commonSupertypeFromReturnTypesOfCallableReference.kt");
}
@TestMetadata("compatibilityWarningOnReferenceAgainstReflectiveType.kt")
public void testCompatibilityWarningOnReferenceAgainstReflectiveType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/compatibilityWarningOnReferenceAgainstReflectiveType.kt");
}
@TestMetadata("constructor.kt")
public void testConstructor() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/constructor.kt");
@@ -2601,6 +2601,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("adaptedReferenceAgainstKCallable.kt")
public void testAdaptedReferenceAgainstKCallable() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstKCallable.kt");
}
@TestMetadata("adaptedReferenceAgainstReflectionType.kt")
public void testAdaptedReferenceAgainstReflectionType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/adaptedReferenceAgainstReflectionType.kt");
}
public void testAllFilesPresentInResolve() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference/resolve"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@@ -2665,6 +2675,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/commonSupertypeFromReturnTypesOfCallableReference.kt");
}
@TestMetadata("compatibilityWarningOnReferenceAgainstReflectiveType.kt")
public void testCompatibilityWarningOnReferenceAgainstReflectiveType() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/compatibilityWarningOnReferenceAgainstReflectiveType.kt");
}
@TestMetadata("constructor.kt")
public void testConstructor() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/resolve/constructor.kt");
@@ -133,6 +133,7 @@ enum class LanguageFeature(
CorrectSourceMappingSyntax(KOTLIN_1_5, kind = UNSTABLE_FEATURE),
ProperArrayConventionSetterWithDefaultCalls(KOTLIN_1_5, kind = OTHER),
DisableCompatibilityModeForNewInference(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED),
AdaptedCallableReferenceAgainstReflectiveType(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED),
// Temporarily disabled, see KT-27084/KT-22379
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),