From 875e2b0cd609954eead58626a53ecceba19556c2 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Tue, 28 Nov 2023 12:08:05 +0200 Subject: [PATCH] [FIR] Prohibit calling functions with dynamic receiver over non-dynamics ^KT-63071 Fixed --- .../api/fir/diagnostics/KtFirDataClassConverters.kt | 7 +++++++ .../analysis/api/fir/diagnostics/KtFirDiagnostics.kt | 5 +++++ .../analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt | 6 ++++++ .../checkers/generator/diagnostics/FirDiagnosticsList.kt | 4 ++++ .../jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt | 1 + .../analysis/diagnostics/FirNonSuppressibleErrorNames.kt | 1 + .../fir/analysis/diagnostics/FirErrorsDefaultMessages.kt | 7 +++++++ .../analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt | 6 +++++- .../org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt | 5 +++++ .../kotlin/fir/resolve/calls/ResolutionDiagnostic.kt | 4 ++++ .../kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt | 6 ++++++ .../testsWithJsStdLib/dynamicTypes/block.fir.kt | 2 +- .../testsWithJsStdLib/dynamicTypes/dynamicExtension.fir.kt | 4 ++-- .../testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt | 4 ++-- .../testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt | 6 +++--- .../testsWithJsStdLib/dynamicTypes/extensions.fir.kt | 4 ++-- 16 files changed, 61 insertions(+), 11 deletions(-) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index ad428dd2f39..e47f137284d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2467,6 +2467,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC) { firDiagnostic -> + DynamicReceiverExpectedButWasNonDynamicImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.INCOMPATIBLE_TYPES) { firDiagnostic -> IncompatibleTypesImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index e54d9fcdf1c..65ba73d6801 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1748,6 +1748,11 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = DynamicReceiverNotAllowed::class } + interface DynamicReceiverExpectedButWasNonDynamic : KtFirDiagnostic { + override val diagnosticClass get() = DynamicReceiverExpectedButWasNonDynamic::class + val actualType: KtType + } + interface IncompatibleTypes : KtFirDiagnostic { override val diagnosticClass get() = IncompatibleTypes::class val typeA: KtType diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index bb720443987..720632e4f3a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2103,6 +2103,12 @@ internal class DynamicReceiverNotAllowedImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.DynamicReceiverNotAllowed +internal class DynamicReceiverExpectedButWasNonDynamicImpl( + override val actualType: KtType, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.DynamicReceiverExpectedButWasNonDynamic + internal class IncompatibleTypesImpl( override val typeA: KtType, override val typeB: KtType, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 7ac3afa183a..4c7c216ddd4 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -810,6 +810,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val DYNAMIC_RECEIVER_NOT_ALLOWED by error() + val DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC by error { + parameter("actualType") + } + val INCOMPATIBLE_TYPES by error { parameter("typeA") parameter("typeB") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index eea1bea415a..36830ec0b6a 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -471,6 +471,7 @@ object FirErrors { val DYNAMIC_SUPERTYPE by error0() val DYNAMIC_UPPER_BOUND by error0() val DYNAMIC_RECEIVER_NOT_ALLOWED by error0() + val DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC by error1() val INCOMPATIBLE_TYPES by error2() val INCOMPATIBLE_TYPES_WARNING by warning2() val TYPE_VARIANCE_CONFLICT_ERROR by error4(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt index 16718a83e1d..aaaa253611a 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt @@ -309,6 +309,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = setOf( "DYNAMIC_SUPERTYPE", "DYNAMIC_UPPER_BOUND", "DYNAMIC_RECEIVER_NOT_ALLOWED", + "DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC", "INCOMPATIBLE_TYPES", "TYPE_VARIANCE_CONFLICT_ERROR", "TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE", diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 2d16113c160..633ebc0bb52 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -205,6 +205,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DSL_SCOPE_VIOLATI import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DUPLICATE_LABEL_IN_WHEN import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DUPLICATE_PARAMETER_NAME_IN_FUNCTION_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_NOT_ALLOWED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_RECEIVER_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_UPPER_BOUND @@ -1467,6 +1468,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(DYNAMIC_RECEIVER_NOT_ALLOWED, "Dynamic receivers are prohibited.") + map.put( + DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC, + "Calling functions expecting the ''dynamic'' receiver over a non-''dynamic'' one such as ''{0}'' is prohibited.", + RENDER_TYPE, + ) + // Reflection map.put( EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, 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 7bc8f521d03..5731d3b8952 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 @@ -93,7 +93,11 @@ private fun ConeDiagnostic.toKtDiagnostic( is ConeTypeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(smallestUnresolvablePrefix.last().source) is ConeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(source) - is ConeInapplicableWrongReceiver -> FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER.createOn(source, this.candidateSymbols) + is ConeInapplicableWrongReceiver -> when (val diagnostic = primaryDiagnostic) { + is DynamicReceiverExpectedButWasNonDynamic -> + FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC.createOn(source, diagnostic.actualType) + else -> FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER.createOn(source, this.candidateSymbols) + } is ConeNoCompanionObject -> FirErrors.NO_COMPANION_OBJECT.createOn(source, this.candidateSymbol as FirClassLikeSymbol<*>) is ConeAmbiguityError -> when { applicability.isSuccess -> FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY.createOn(source, this.candidates.map { it.symbol }) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 53f7d1c7fae..cdb59c1bdba 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -392,6 +392,11 @@ private fun checkApplicabilityForArgumentType( return } + if (isReceiver && expectedType is ConeDynamicType && argumentType !is ConeDynamicType) { + sink.reportDiagnostic(DynamicReceiverExpectedButWasNonDynamic(argumentType)) + return + } + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { val smartcastExpression = argument as? FirSmartCastExpression if (smartcastExpression != null && !smartcastExpression.isStable) { diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index f95179025af..0817b6c378c 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -93,6 +93,10 @@ class InapplicableWrongReceiver( val actualType: ConeKotlinType? = null, ) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER) +class DynamicReceiverExpectedButWasNonDynamic( + val actualType: ConeKotlinType, +) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER) + object NoCompanionObject : ResolutionDiagnostic(K2_NO_COMPANION_OBJECT) class UnsafeCall(val actualType: ConeKotlinType) : ResolutionDiagnostic(UNSAFE_CALL) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt index 2cb6b7b3b83..94cc501a127 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.expressions.FirOperation import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate +import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.ConeKotlinType @@ -120,6 +121,11 @@ class ConeInapplicableWrongReceiver(override val candidates: Collection dynamic(body: dynamic.() -> T): T { val topLevel = null - return topLevel.body() + return topLevel.body() } diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.fir.kt index 6b30ca46bfa..9140b97ec89 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.fir.kt @@ -21,8 +21,8 @@ fun test(d: dynamic, a: Any?) { println(item) } - a.onDynamicFun() - a.onDynamicProperty + a.onDynamicFun() + a.onDynamicProperty } fun eatT(t: T) {} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt index ebc8d05d636..dc9b17b242f 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.fir.kt @@ -24,7 +24,7 @@ fun test(d: dynamic) { (d as String).onStringVal (d as Any).onAnyVal (d as Any?).onNullableAnyVal - (d as Any).onDynamicVal + (d as Any).onDynamicVal } fun testReassignmentWithSafeCall(d: dynamic) { @@ -35,7 +35,7 @@ fun testReassignmentWithStaticCalls(d: dynamic) { (d as String).onStringVal = 1 (d as Any).onAnyVal = 1 (d as Any?).onNullableAnyVal = 1 - (d as Any).onDynamicVal = 1 + (d as Any).onDynamicVal = 1 } val Any.onAnyVal: Int get() = 1 diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt index e1e0972cf37..a299d9d8e53 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.fir.kt @@ -22,17 +22,17 @@ fun test(d: dynamic) { d.onDynamicVar = 1 - d?.onDynamicVar = 1 + d?.onDynamicVar = 1 (d as String).onStringVar (d as Any).onAnyVar (d as Any?).onNullableAnyVar - (d as Any).onDynamicVar + (d as Any).onDynamicVar (d as String).onStringVar = 1 (d as Any).onAnyVar = 1 (d as Any?).onNullableAnyVar = 1 - (d as Any).onDynamicVar = 1 + (d as Any).onDynamicVar = 1 } var Any.onAnyVar: Int diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt index 2f9cc32d4db..38bd17c9928 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.fir.kt @@ -14,12 +14,12 @@ fun test(d: dynamic) { d.onString() d.onDynamic() - d?.onDynamic() + d?.onDynamic() (d as String).onString() (d as Any).onAny() (d as Any?).onNullableAny() - (d as Any).onDynamic() + (d as Any).onDynamic() } fun Any.onAny() {}