From bb27ae2b4215979417c70b28297cc04e0c1e5d5c Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Thu, 19 Aug 2021 13:11:47 +0300 Subject: [PATCH] [FIR] Fix incorrect resolve of callable reference in function signature (^KT-48304 Fixed) --- .../references/referenceToField.fir.txt | 2 +- .../expression/FirCallableReferenceChecker.kt | 10 --------- .../coneDiagnosticToFirDiagnostic.kt | 2 +- .../jetbrains/kotlin/fir/FirCallResolver.kt | 7 +++++- .../fir/resolve/calls/CandidateFactory.kt | 15 ++++++++++--- .../fir/resolve/calls/ResolutionDiagnostic.kt | 3 +++ .../fir/resolve/diagnostics/FirDiagnostics.kt | 6 +++-- .../calls/tower/CandidateApplicability.kt | 1 + .../callableReferenceToLocalVariable.fir.kt | 22 +++++++++++++++++++ .../callableReferenceToLocalVariable.kt | 15 ++++++++++++- .../callableReferenceToLocalVariable.txt | 13 +++++++++++ .../unsupported/localVariable.fir.kt | 15 +++++++++++++ .../unsupported/localVariable.kt | 1 - .../localVariableWithSubstitution.fir.kt | 2 +- .../parameterWithSubstitution.fir.kt | 2 +- .../callableReferenceOnParameter.fir.kt | 2 +- 16 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt create mode 100644 compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/references/referenceToField.fir.txt b/compiler/fir/analysis-tests/testData/resolve/references/referenceToField.fir.txt index c65c3d19629..0093561c6d5 100644 --- a/compiler/fir/analysis-tests/testData/resolve/references/referenceToField.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/references/referenceToField.fir.txt @@ -6,7 +6,7 @@ FILE: referenceToField.kt public final val x: R|kotlin/Int| = Int(1) public get(): R|kotlin/Int| { - ::F|/A.x| + ::# ^ this@R|/A|.F|/A.x| } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt index 1d219575bf3..ec67d1db3de 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt @@ -50,15 +50,5 @@ object FirCallableReferenceChecker : FirQualifiedAccessExpressionChecker() { ) { reporter.reportOn(source, FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, referredSymbol, context) } - // The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor. - // Here, we explicitly check if the referred declaration/symbol is value parameter, local variable, or backing field. - if ( - referredSymbol is FirValueParameterSymbol || - (referredSymbol is FirPropertySymbol && referredSymbol.isLocal) || - reference.resolvedSymbol is FirBackingFieldSymbol - ) { - // TODO: we can't set positioning strategy to meta error. Should report on reference expression, not entire reference access. - reporter.reportOn(source, FirErrors.UNSUPPORTED, "References to variables aren't supported yet", context) - } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 0fcbd0057d5..71098f3a305 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -89,7 +89,7 @@ private fun ConeDiagnostic.toFirDiagnostic( is ConeTypeParameterInQualifiedAccess -> null // reported in various checkers instead is ConeNotAnnotationContainer -> null is ConeImportFromSingleton -> FirErrors.CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON.createOn(source, this.name) - is ConeUnsupportedDynamicType -> FirErrors.UNSUPPORTED.createOn(source, this.reason) + is ConeUnsupported -> FirErrors.UNSUPPORTED.createOn(this.source ?: source, this.reason) is ConeLocalVariableNoTypeOrInitializer -> runIf(variable.isLocalMember) { FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.createOn(source) } is ConeUnderscoreIsReserved -> FirErrors.UNDERSCORE_IS_RESERVED.createOn(this.source) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index b1ad0189f31..876d42617d3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -378,7 +378,12 @@ class FirCallResolver( noSuccessfulCandidates -> { val errorReference = buildErrorReference( info, - ConeUnresolvedReferenceError(info.name), + if (applicability == CandidateApplicability.UNSUPPORTED) { + val unsupportedResolutionDiagnostic = reducedCandidates.firstOrNull()?.diagnostics?.firstOrNull() as? Unsupported + ConeUnsupported(unsupportedResolutionDiagnostic?.message ?: "", unsupportedResolutionDiagnostic?.source) + } else { + ConeUnresolvedReferenceError(info.name) + }, callableReferenceAccess.source ) resolvedCallableReferenceAtom.resultingReference = errorReference diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index f111a9bbe9e..1031985d759 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -17,8 +17,7 @@ import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.returnExpressions import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirErrorFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirErrorPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage @@ -51,7 +50,7 @@ class CandidateFactory private constructor( extensionReceiverValue: ReceiverValue? = null, builtInExtensionFunctionReceiverValue: ReceiverValue? = null ): Candidate { - return Candidate( + val result = Candidate( symbol, dispatchReceiverValue, extensionReceiverValue, explicitReceiverKind, context.inferenceComponents.constraintSystemFactory, baseSystem, builtInExtensionFunctionReceiverValue?.receiverExpression?.let { @@ -65,6 +64,16 @@ class CandidateFactory private constructor( ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> false } ) + + // The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor. + // Here, we explicitly check if the referred declaration/symbol is value parameter, local variable, or backing field. + val callSite = callInfo.callSite + if (callSite is FirCallableReferenceAccess) { + if (symbol is FirValueParameterSymbol || symbol is FirPropertySymbol && symbol.isLocal || symbol is FirBackingFieldSymbol) { + result.addDiagnostic(Unsupported("References to variables aren't supported yet", callSite.calleeReference.source)) + } + } + return result } private fun ReceiverValue?.isCandidateFromCompanionObjectTypeScope(): Boolean { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index d42eece5a01..3a0bbe3437a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.resolve.calls +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression @@ -94,3 +95,5 @@ class ManyLambdaExpressionArguments( class InfixCallOfNonInfixFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER) class OperatorCallOfNonOperatorFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER) + +class Unsupported(val message: String, val source: FirSourceElement? = null) : ResolutionDiagnostic(UNSUPPORTED) \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt index f7e05c028c6..6b7c31523da 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt @@ -156,10 +156,12 @@ class ConeImportFromSingleton(val name: Name) : ConeDiagnostic() { override val reason: String get() = "Import from singleton $name is not allowed" } -class ConeUnsupportedDynamicType() : ConeDiagnostic() { - override val reason: String get() = "Dynamic types are not supported in this context" +open class ConeUnsupported(val message: String, val source: FirSourceElement? = null) : ConeDiagnostic() { + override val reason: String get() = message } +class ConeUnsupportedDynamicType : ConeUnsupported("Dynamic types are not supported in this context") + class ConeDeprecated(val source: FirSourceElement?, val symbol: FirBasedSymbol<*>, val deprecation: Deprecation) : ConeDiagnostic() { override val reason: String get() = "Deprecated: ${deprecation.message}" } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt index 08c6b7240ab..653d478e27d 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve.calls.tower enum class CandidateApplicability { RESOLVED_TO_SAM_WITH_VARARG, // migration warning up to 1.5 (when resolve to function with SAM conversion and array without spread as vararg) HIDDEN, // removed from resolve + UNSUPPORTED, // unsupported feature INAPPLICABLE_WRONG_RECEIVER, // receiver not matched INAPPLICABLE_ARGUMENTS_MAPPING_ERROR, // arguments not mapped to parameters (i.e. different size of arguments and parameters) INAPPLICABLE, // arguments have wrong types diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt new file mode 100644 index 00000000000..c0d6ca08f37 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + + +fun a() { + val x = 10 + foo(::x) +} + +fun foo(a: Any) {} + +fun test1(test2: () -> Unit = ::test2) {} // Resolve to function +private fun test2() {} +fun test3(test4: () -> Unit = ::test4) {} + +fun test5(test6: (test: Test) -> Unit = Test::helper) { + test6(Test()) +} + +class Test { + fun helper() {} +} + diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt index 6830eef3b8a..52bb500a641 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt @@ -1,9 +1,22 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + fun a() { val x = 10 foo(::x) } fun foo(a: Any) {} + +fun test1(test2: () -> Unit = ::test2) {} // Resolve to function +private fun test2() {} +fun test3(test4: () -> Unit = ::test4) {} + +fun test5(test6: (test: Test) -> Unit = Test::helper) { + test6(Test()) +} + +class Test { + fun helper() {} +} + diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.txt b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.txt index f05b52a12b4..d7dc2b62d55 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.txt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.txt @@ -2,3 +2,16 @@ package public fun a(): kotlin.Unit public fun foo(/*0*/ a: kotlin.Any): kotlin.Unit +public fun test1(/*0*/ test2: () -> kotlin.Unit = ...): kotlin.Unit +private fun test2(): kotlin.Unit +public fun test3(/*0*/ test4: () -> kotlin.Unit = ...): kotlin.Unit +public fun test5(/*0*/ test6: (test: Test) -> kotlin.Unit = ...): kotlin.Unit + +public final class Test { + public constructor Test() + 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 helper(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt new file mode 100644 index 00000000000..a5d4d4e6293 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt @@ -0,0 +1,15 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE + +fun eat(value: Any) {} + +fun test(param: String) { + val a = ::param + + val local = "local" + val b = ::local + + val lambda = { -> } + val g = ::lambda + + eat(::param) +} diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.kt index 9e8674acb1b..3639382638e 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE fun eat(value: Any) {} diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt index 00bb2ef13b4..c1ae03c3a3f 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt @@ -9,6 +9,6 @@ class Foo { fun main() { val f = Foo() val a: Int - get() = f.getValue(null, ::a) // no exception after fix + get() = f.getValue(null, ::a) // no exception after fix print(a) } diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt index 7ef33a78ec2..a59813ccbe6 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt @@ -9,6 +9,6 @@ class Foo { fun main(x: Int) { val f = Foo() val a: Int - get() = f.getValue(null, ::x) // no exception after fix + get() = f.getValue(null, ::x) // no exception after fix print(a) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/callableReferenceOnParameter.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/callableReferenceOnParameter.fir.kt index f4ed2812b6d..35a458694cb 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/inference/callableReferenceOnParameter.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/callableReferenceOnParameter.fir.kt @@ -3,6 +3,6 @@ internal class Z { val map = HashMap() inline fun compute(key: String, producer: () -> String): String { - return map.getOrPut(key, ::producer) + return map.getOrPut(key, ::producer) } }