From 4cf8d9ffb98a6636b5370da629a667f28abefdc9 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Wed, 4 Jan 2023 15:37:15 +0200 Subject: [PATCH] [FIR JS] Implement FirJsInheritanceChecker --- .../diagnostics/KtFirDataClassConverters.kt | 19 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 13 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 16 +++++ .../diagnostics/FirJsDiagnosticsList.kt | 13 ++++ .../analysis/diagnostics/js/FirJsErrors.kt | 9 +++ .../js/FirJsErrorsDefaultMessages.kt | 10 +++ .../fir/analysis/js/checkers/FirJsHelpers.kt | 18 +++++ .../js/checkers/JsDeclarationCheckers.kt | 2 + .../FirJsInheritanceClassChecker.kt | 72 +++++++++++++++++++ .../FirJsInheritanceFunctionChecker.kt | 29 ++++++++ .../fir/analysis/checkers/FirHelpers.kt | 37 +++++----- .../scopes/FirContainingNamesAwareScope.kt | 6 ++ .../funInterface/kt46908_functionSupertype.kt | 1 + .../functions/invoke/kt3822invokeOnThis.kt | 1 + .../implementingFunction.fir.kt | 9 --- .../testsWithJsStdLib/implementingFunction.kt | 1 + .../native/overrideOptionalParam.fir.kt | 8 +-- 17 files changed, 235 insertions(+), 29 deletions(-) create mode 100644 compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceClassChecker.kt create mode 100644 compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceFunctionChecker.kt delete mode 100644 compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.fir.kt 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 b3b44d13479..3cc1e43d849 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 @@ -4648,6 +4648,25 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE) { firDiagnostic -> + ImplementingFunctionInterfaceImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS) { firDiagnostic -> + OverridingExternalFunWithOptionalParamsImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE) { firDiagnostic -> + OverridingExternalFunWithOptionalParamsWithFakeImpl( + firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(firDiagnostic.a), + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJsErrors.DELEGATION_BY_DYNAMIC) { firDiagnostic -> DelegationByDynamicImpl( firDiagnostic as KtPsiDiagnostic, 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 f90620ff495..68bfd193d25 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 @@ -3233,6 +3233,19 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = JavaSamInterfaceConstructorReference::class } + abstract class ImplementingFunctionInterface : KtFirDiagnostic() { + override val diagnosticClass get() = ImplementingFunctionInterface::class + } + + abstract class OverridingExternalFunWithOptionalParams : KtFirDiagnostic() { + override val diagnosticClass get() = OverridingExternalFunWithOptionalParams::class + } + + abstract class OverridingExternalFunWithOptionalParamsWithFake : KtFirDiagnostic() { + override val diagnosticClass get() = OverridingExternalFunWithOptionalParamsWithFake::class + abstract val function: KtFunctionLikeSymbol + } + abstract class DelegationByDynamic : KtFirDiagnostic() { override val diagnosticClass get() = DelegationByDynamic::class } 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 dbd594e1d45..34ad7a8c355 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 @@ -3911,6 +3911,22 @@ internal class JavaSamInterfaceConstructorReferenceImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.JavaSamInterfaceConstructorReference(), KtAbstractFirDiagnostic +internal class ImplementingFunctionInterfaceImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ImplementingFunctionInterface(), KtAbstractFirDiagnostic + +internal class OverridingExternalFunWithOptionalParamsImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.OverridingExternalFunWithOptionalParams(), KtAbstractFirDiagnostic + +internal class OverridingExternalFunWithOptionalParamsWithFakeImpl( + override val function: KtFunctionLikeSymbol, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.OverridingExternalFunWithOptionalParamsWithFake(), KtAbstractFirDiagnostic + internal class DelegationByDynamicImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt index bf63dd23610..4be8eaafc5f 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJsDiagnosticsList.kt @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement @Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused") @@ -27,6 +29,17 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") { } } + val FUN_INTERFACES by object : DiagnosticGroup("Fun Interfaces") { + val IMPLEMENTING_FUNCTION_INTERFACE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + } + + val EXTERNAL by object : DiagnosticGroup("External") { + val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) + val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE by error(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) { + parameter("function") + } + } + val DYNAMICS by object : DiagnosticGroup("Dynamics") { val DELEGATION_BY_DYNAMIC by error() } diff --git a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt index 12828f7879c..7aa7b325f20 100644 --- a/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt +++ b/compiler/fir/checkers/checkers.js/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrors.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement /* @@ -27,6 +29,13 @@ object FirJsErrors { // Supertypes val WRONG_MULTIPLE_INHERITANCE by error1>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + // Fun Interfaces + val IMPLEMENTING_FUNCTION_INTERFACE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + + // External + val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + // Dynamics val DELEGATION_BY_DYNAMIC by error0() diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt index c1460c95c97..7f12e2f0a6f 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/diagnostics/js/FirJsErrorsDefaultMessages.kt @@ -10,9 +10,12 @@ import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers import org.jetbrains.kotlin.fir.analysis.diagnostics.checkMissingMessages import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.DELEGATION_BY_DYNAMIC +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_NON_NATIVE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_VAR import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_MODULE_PROHIBITED +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_MULTIPLE_INHERITANCE @@ -32,6 +35,13 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() { FirDiagnosticRenderers.SYMBOL ) map.put(DELEGATION_BY_DYNAMIC, "Can't delegate to dynamic value") + map.put(IMPLEMENTING_FUNCTION_INTERFACE, "Implementing function interface is prohibited in JavaScript") + map.put(OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS, "Overriding `external` function with optional parameters") + map.put( + OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE, + "Overriding `external` function with optional parameters by declaration from superclass: {0}", + FirDiagnosticRenderers.SYMBOL + ) map.checkMissingMessages(FirJsErrors) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt index bbe3db5fef7..75c0d7f0603 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/FirJsHelpers.kt @@ -7,12 +7,16 @@ package org.jetbrains.kotlin.fir.analysis.js.checkers +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenFunctions import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol import org.jetbrains.kotlin.fir.analysis.checkers.hasAnnotationOrInsideAnnotatedClass import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isExternal +import org.jetbrains.kotlin.fir.declarations.utils.modality +import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.* @@ -42,6 +46,20 @@ fun FirBasedSymbol<*>.isEffectivelyExternal(session: FirSession): Boolean { return getContainingClassSymbol(session)?.isEffectivelyExternal(session) == true } +fun FirBasedSymbol<*>.isEffectivelyExternal(context: CheckerContext) = isEffectivelyExternal(context.session) + +fun FirFunctionSymbol<*>.isOverridingExternalWithOptionalParams(context: CheckerContext): Boolean { + if (!isSubstitutionOrIntersectionOverride && modality == Modality.ABSTRACT) return false + + val overridden = (this as? FirNamedFunctionSymbol)?.directOverriddenFunctions(context) ?: return false + + for (overriddenFunction in overridden.filter { it.isEffectivelyExternal(context) }) { + if (overriddenFunction.valueParameterSymbols.any { it.hasDefaultValue }) return true + } + + return false +} + fun FirBasedSymbol<*>.isNativeObject(session: FirSession): Boolean { if (hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsNative, session) || isEffectivelyExternal(session)) { return true diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt index 7464171a919..12dbc3115bf 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt @@ -12,6 +12,7 @@ object JsDeclarationCheckers : DeclarationCheckers() { override val functionCheckers: Set get() = setOf( FirJsInlineDeclarationChecker, + FirJsInheritanceFunctionChecker, ) override val propertyCheckers: Set @@ -28,5 +29,6 @@ object JsDeclarationCheckers : DeclarationCheckers() { get() = setOf( FirJsMultipleInheritanceChecker, FirJsDynamicDeclarationChecker, + FirJsInheritanceClassChecker, ) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceClassChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceClassChecker.kt new file mode 100644 index 00000000000..795233f8463 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceClassChecker.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol +import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors +import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal +import org.jetbrains.kotlin.fir.analysis.js.checkers.isOverridingExternalWithOptionalParams +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes +import org.jetbrains.kotlin.fir.scopes.collectAllFunctions +import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.typeContext + +object FirJsInheritanceClassChecker : FirClassChecker() { + override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { + if (!declaration.symbol.isEffectivelyExternal(context)) { + val fakeOverriddenMethod = declaration.findFakeMethodOverridingExternalWithOptionalParams(context) + + if (fakeOverriddenMethod != null) { + reporter.reportOn( + declaration.source, FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE, + fakeOverriddenMethod, context + ) + } + } + + if ( + !context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowImplementingFunctionInterface) && + declaration.superConeTypes.any { + it.isBuiltinFunctionalTypeOrSubtype(context.session) && !it.isSuspendFunctionTypeOrSubtype(context.session) + } + ) { + reporter.reportOn(declaration.source, FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE, context) + } + } + + private fun ConeClassLikeType.isBuiltinFunctionalTypeOrSubtype(session: FirSession): Boolean { + return with(session.typeContext) { isBuiltinFunctionalTypeOrSubtype() } + } + + private fun ConeClassLikeType.isSuspendFunctionTypeOrSubtype(session: FirSession): Boolean { + return with(session.typeContext) { isSuspendFunctionTypeOrSubtype() } + } + + private fun FirClass.findFakeMethodOverridingExternalWithOptionalParams(context: CheckerContext): FirNamedFunctionSymbol? { + val scope = symbol.unsubstitutedScope(context) + + val members = scope.collectAllFunctions() + .filterIsInstance() + .filter { + val container = it.getContainingClassSymbol(context.session) + container == symbol && it.intersections.isNotEmpty() + } + + return members.firstOrNull { + it.isOverridingExternalWithOptionalParams(context) + } + } +} diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceFunctionChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceFunctionChecker.kt new file mode 100644 index 00000000000..dd727a210d5 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsInheritanceFunctionChecker.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker +import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal +import org.jetbrains.kotlin.fir.analysis.js.checkers.isOverridingExternalWithOptionalParams +import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirFunction + +object FirJsInheritanceFunctionChecker : FirFunctionChecker() { + override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration.isNotEffectivelyExternalFunctionButOverridesExternal(context)) { + reporter.reportOn(declaration.source, FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS, context) + } + } + + private fun FirDeclaration.isNotEffectivelyExternalFunctionButOverridesExternal(context: CheckerContext): Boolean { + if (this !is FirFunction || symbol.isEffectivelyExternal(context)) return false + return symbol.isOverridingExternalWithOptionalParams(context) + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index dd12c01f440..2a72cdb8243 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -14,34 +14,26 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.diagnostics.* -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.getChild -import org.jetbrains.kotlin.fir.containingClassForLocalAttr -import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol -import org.jetbrains.kotlin.fir.resolve.SessionHolder -import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass +import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol -import org.jetbrains.kotlin.fir.resolve.toSymbol -import org.jetbrains.kotlin.fir.scopes.FirTypeScope -import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature -import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions -import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.unwrapFakeOverrides import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.KtParameter @@ -138,11 +130,13 @@ fun FirTypeRef.toRegularClassSymbol(session: FirSession): FirRegularClassSymbol? * Returns the ClassLikeDeclaration where the Fir object has been defined * or null if no proper declaration has been found. */ -fun FirDeclaration.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? = - (this as? FirCallableDeclaration)?.containingClassLookupTag()?.toSymbol(session) +fun FirBasedSymbol<*>.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? = when (this) { + is FirCallableSymbol<*> -> containingClassLookupTag()?.toSymbol(session) + is FirClassLikeSymbol<*> -> getContainingClassLookupTag()?.toSymbol(session) + else -> null +} -@OptIn(SymbolInternals::class) -fun FirBasedSymbol<*>.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? = fir.getContainingClassSymbol(session) +fun FirDeclaration.getContainingClassSymbol(session: FirSession) = symbol.getContainingClassSymbol(session) fun FirClassLikeSymbol<*>.outerClassSymbol(context: CheckerContext): FirClassLikeSymbol<*>? { if (this !is FirClassSymbol<*>) return null @@ -708,6 +702,17 @@ fun ConeKotlinType.getInlineClassUnderlyingType(session: FirSession): ConeKotlin return toRegularClassSymbol(session)!!.primaryConstructorSymbol()!!.valueParameterSymbols[0].resolvedReturnTypeRef.coneType } +fun FirNamedFunctionSymbol.directOverriddenFunctions(session: FirSession, scopeSession: ScopeSession): List { + val classSymbol = getContainingClassSymbol(session) as? FirClassSymbol ?: return emptyList() + val scope = classSymbol.unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false) + + scope.processFunctionsByName(name) { } + return scope.getDirectOverriddenFunctions(this, true) +} + +fun FirNamedFunctionSymbol.directOverriddenFunctions(context: CheckerContext) = + directOverriddenFunctions(context.session, context.sessionHolder.scopeSession) + fun CheckerContext.closestNonLocalWith(declaration: FirDeclaration) = (containingDeclarations + declaration).takeWhile { it.isNonLocal }.lastOrNull() diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt index 51724d3d320..2dd52cf8325 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt @@ -32,3 +32,9 @@ fun FirContainingNamesAwareScope.collectAllProperties(): Collection { + return mutableListOf().apply { + processAllFunctions(this::add) + } +} diff --git a/compiler/testData/codegen/box/funInterface/kt46908_functionSupertype.kt b/compiler/testData/codegen/box/funInterface/kt46908_functionSupertype.kt index 876d6556265..f078c42a3b8 100644 --- a/compiler/testData/codegen/box/funInterface/kt46908_functionSupertype.kt +++ b/compiler/testData/codegen/box/funInterface/kt46908_functionSupertype.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_K1: JS, JS_IR +// IGNORE_BACKEND_K2: JS_IR fun interface Foo : () -> Int diff --git a/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt b/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt index 7fb2ba167fd..7bb0ab3a042 100644 --- a/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt +++ b/compiler/testData/codegen/box/functions/invoke/kt3822invokeOnThis.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND_K1: JS_IR +// IGNORE_BACKEND_K2: JS_IR // IGNORE_BACKEND: JS_IR_ES6 //KT-3822 Compiler crashes when use invoke convention with `this` in class which extends Function0 // IGNORE_BACKEND: JS diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.fir.kt deleted file mode 100644 index dd0991decff..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -abstract class A : () -> Unit - -object B : (String, Int) -> Long { - override fun invoke(a: String, B: Int) = 23L -} - -abstract class C : kotlin.Function1 - -abstract class D : C() diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.kt b/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.kt index 375553c5ecd..845c348ec84 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/implementingFunction.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL abstract class A : () -> Unit object B : (String, Int) -> Long { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.fir.kt index 5fe95a5fae8..afe00bc7d32 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.fir.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/native/overrideOptionalParam.fir.kt @@ -3,7 +3,7 @@ open external class A { } class B : A() { - override fun f(x: Int) {} + override fun f(x: Int) {} } class BB : A() @@ -36,7 +36,7 @@ class E : D() { } class F : D(), I { - override fun f(x: Int) {} + override fun f(x: Int) {} } external class G : D, I { @@ -51,9 +51,9 @@ open external class XE { fun f(x: Int) } -class Y : X(), I +class Y : X(), I -class YY : A(), II +class YY : A(), II external class YE: XE, I