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 74ec8e35bf3..1fa1b252d5e 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 @@ -3252,6 +3252,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.EXPECTED_EXTERNAL_DECLARATION) { firDiagnostic -> + ExpectedExternalDeclarationImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirErrors.EXPECTED_TAILREC_FUNCTION) { firDiagnostic -> + ExpectedTailrecFunctionImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS) { firDiagnostic -> ImplementationByDelegationInExpectClassImpl( 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 c7291ef151d..caa404c7ff0 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 @@ -2290,6 +2290,14 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ExpectedPrivateDeclaration::class } + abstract class ExpectedExternalDeclaration : KtFirDiagnostic() { + override val diagnosticClass get() = ExpectedExternalDeclaration::class + } + + abstract class ExpectedTailrecFunction : KtFirDiagnostic() { + override val diagnosticClass get() = ExpectedTailrecFunction::class + } + abstract class ImplementationByDelegationInExpectClass : KtFirDiagnostic() { override val diagnosticClass get() = ImplementationByDelegationInExpectClass::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 d81fbf7b378..45d5d7507b5 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 @@ -2761,6 +2761,16 @@ internal class ExpectedPrivateDeclarationImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ExpectedPrivateDeclaration(), KtAbstractFirDiagnostic +internal class ExpectedExternalDeclarationImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ExpectedExternalDeclaration(), KtAbstractFirDiagnostic + +internal class ExpectedTailrecFunctionImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ExpectedTailrecFunction(), KtAbstractFirDiagnostic + internal class ImplementationByDelegationInExpectClassImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index a749607bd97..a48d39255ca 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -31,6 +31,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/expectDataObject.kt"); } + @Test + @TestMetadata("expectExternal.kt") + public void testExpectExternal() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt"); + } + @Test @TestMetadata("expectInterfaceApplicability.kt") public void testExpectInterfaceApplicability() throws Exception { @@ -43,6 +49,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt"); } + @Test + @TestMetadata("expectTailrec.kt") + public void testExpectTailrec() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt"); + } + @Test @TestMetadata("headerFunInNonHeaderClass.kt") public void testHeaderFunInNonHeaderClass() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index 2c0d779f6d8..2733531a22c 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -31,6 +31,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/expectDataObject.kt"); } + @Test + @TestMetadata("expectExternal.kt") + public void testExpectExternal() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt"); + } + @Test @TestMetadata("expectInterfaceApplicability.kt") public void testExpectInterfaceApplicability() throws Exception { @@ -43,6 +49,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt"); } + @Test + @TestMetadata("expectTailrec.kt") + public void testExpectTailrec() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt"); + } + @Test @TestMetadata("headerFunInNonHeaderClass.kt") public void testHeaderFunInNonHeaderClass() throws Exception { 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 e7104769364..b7266b7f716 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 @@ -1104,6 +1104,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val EXPECTED_LATEINIT_PROPERTY by error(PositioningStrategy.LATEINIT_MODIFIER) val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error() val EXPECTED_PRIVATE_DECLARATION by error(PositioningStrategy.VISIBILITY_MODIFIER) + val EXPECTED_EXTERNAL_DECLARATION by error(PositioningStrategy.EXTERNAL_MODIFIER) + val EXPECTED_TAILREC_FUNCTION by error(PositioningStrategy.TAILREC_MODIFIER) val IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS by error() val ACTUAL_TYPE_ALIAS_NOT_TO_CLASS by error(PositioningStrategy.DECLARATION_SIGNATURE) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index d96b53e2bd2..d79b74aef84 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -107,6 +107,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { ENUM_MODIFIER, FIELD_KEYWORD, TAILREC_MODIFIER, + EXTERNAL_MODIFIER, PROPERTY_DELEGATE, IMPORT_ALIAS, DECLARATION_START_TO_NAME, 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 15a0bff50bd..7c22b7e0f58 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 @@ -599,6 +599,8 @@ object FirErrors { val EXPECTED_LATEINIT_PROPERTY by error0(SourceElementPositioningStrategies.LATEINIT_MODIFIER) val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error0() val EXPECTED_PRIVATE_DECLARATION by error0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) + val EXPECTED_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.EXTERNAL_MODIFIER) + val EXPECTED_TAILREC_FUNCTION by error0(SourceElementPositioningStrategies.TAILREC_MODIFIER) val IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS by error0() val ACTUAL_TYPE_ALIAS_NOT_TO_CLASS by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index 9aa80ba43fe..37da4ad2810 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration +import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind @@ -15,6 +16,9 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isActual +import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.declarations.utils.isExternal +import org.jetbrains.kotlin.fir.declarations.utils.isTailRec import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -33,11 +37,60 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (!context.session.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return if (declaration !is FirMemberDeclaration) return + if (declaration.isExpect) { + checkExpectDeclarationModifiers(declaration, context, reporter) + } if (declaration.isActual) { checkActualDeclarationHasExpected(declaration, context, reporter) } } + private fun checkExpectDeclarationModifiers( + declaration: FirMemberDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + checkExpectDeclarationHasNoExternalModifier(declaration, context, reporter) + if (declaration is FirProperty) { + checkExpectPropertyAccessorsModifiers(declaration, context, reporter) + } + if (declaration is FirFunction && declaration.isTailRec) { + reporter.reportOn(declaration.source, FirErrors.EXPECTED_TAILREC_FUNCTION, context) + } + } + + private fun checkExpectPropertyAccessorsModifiers( + property: FirProperty, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + for (accessor in listOfNotNull(property.getter, property.setter)) { + checkExpectPropertyAccessorModifiers(accessor, context, reporter) + } + } + + private fun checkExpectPropertyAccessorModifiers( + accessor: FirPropertyAccessor, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + fun FirPropertyAccessor.isDefault() = source?.kind == KtFakeSourceElementKind.DefaultAccessor + + if (!accessor.isDefault()) { + checkExpectDeclarationHasNoExternalModifier(accessor, context, reporter) + } + } + + private fun checkExpectDeclarationHasNoExternalModifier( + declaration: FirMemberDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + if (declaration.isExternal) { + reporter.reportOn(declaration.source, FirErrors.EXPECTED_EXTERNAL_DECLARATION, context) + } + } + private fun checkActualDeclarationHasExpected( declaration: FirMemberDeclaration, context: CheckerContext, 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 65c3b70ce49..ac8e6460946 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 @@ -223,6 +223,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FLOAT_LITERAL_OUT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_BINARY_MOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PROPERTY_TYPE_IN_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_IDENTITY_EQUALS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_EXTERNAL_DECLARATION +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_TAILREC_FUNCTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_BINARY_MOD import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_IDENTITY_EQUALS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_IDENTITY_EQUALS_WARNING @@ -1762,6 +1764,8 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(EXPECTED_DELEGATED_PROPERTY, "Expected property cannot be delegated") map.put(EXPECTED_LATEINIT_PROPERTY, "Expected property cannot be lateinit") map.put(EXPECTED_PRIVATE_DECLARATION, "Expected declaration cannot be private") + map.put(EXPECTED_EXTERNAL_DECLARATION, "Expected declaration cannot be external") + map.put(EXPECTED_TAILREC_FUNCTION, "Expected function cannot have 'tailrec' modifier") map.put(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, "Expected classes cannot initialize supertypes") map.put(IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS, "Implementation by delegation in expected classes is prohibited") map.put(ACTUAL_TYPE_ALIAS_NOT_TO_CLASS, "Right-hand side of actual type alias should be a class, not another type alias") diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt index c5de0d134d3..1193bd1954a 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt @@ -455,6 +455,9 @@ object LightTreePositioningStrategies { val TAILREC_MODIFIER: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(KtTokens.TAILREC_KEYWORD) + val EXTERNAL_MODIFIER: LightTreePositioningStrategy = + ModifierSetBasedLightTreePositioningStrategy(KtTokens.EXTERNAL_KEYWORD) + val OBJECT_KEYWORD: LightTreePositioningStrategy = keywordStrategy { objectKeyword(it) } val FIELD_KEYWORD: LightTreePositioningStrategy = keywordStrategy { fieldKeyword(it) } diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index d2aaef3fa33..b398f574af0 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -309,6 +309,10 @@ object PositioningStrategies { val TAILREC_MODIFIER: PositioningStrategy = ModifierSetBasedPositioningStrategy(KtTokens.TAILREC_KEYWORD) + @JvmField + val EXTERNAL_MODIFIER: PositioningStrategy = + ModifierSetBasedPositioningStrategy(KtTokens.EXTERNAL_KEYWORD) + @JvmField val OBJECT_KEYWORD: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtObjectDeclaration): List { diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt index 7b4956dfcd6..3d74c4579f3 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt @@ -340,6 +340,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.TAILREC_MODIFIER ) + val EXTERNAL_MODIFIER = SourceElementPositioningStrategy( + LightTreePositioningStrategies.EXTERNAL_MODIFIER, + PositioningStrategies.EXTERNAL_MODIFIER + ) + val PROPERTY_DELEGATE = SourceElementPositioningStrategy( LightTreePositioningStrategies.PROPERTY_DELEGATE, PositioningStrategies.PROPERTY_DELEGATE diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 3a494b85d4f..614f4e75883 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -790,6 +790,8 @@ public interface Errors { DiagnosticFactory0 EXPECTED_LATEINIT_PROPERTY = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 EXPECTED_PRIVATE_DECLARATION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 EXPECTED_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 EXPECTED_TAILREC_FUNCTION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 ACTUAL_TYPE_ALIAS_NOT_TO_CLASS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 30d7eec62c4..e2664cfa15a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -352,6 +352,8 @@ public class DefaultErrorMessages { MAP.put(EXPECTED_LATEINIT_PROPERTY, "Expected property cannot be lateinit"); MAP.put(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, "Expected classes cannot initialize supertypes"); MAP.put(EXPECTED_PRIVATE_DECLARATION, "Expected declaration cannot be private"); + MAP.put(EXPECTED_EXTERNAL_DECLARATION, "Expected declaration cannot be external"); + MAP.put(EXPECTED_TAILREC_FUNCTION, "Expected function cannot have 'tailrec' modifier"); MAP.put(IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS, "Implementation by delegation in expected classes is prohibited"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index fcf548dec98..7f0594dcf79 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -343,7 +343,7 @@ class DeclarationsChecker( checkPrimaryConstructor(classOrObject, classDescriptor) - checkPrivateExpectedDeclaration(classOrObject, classDescriptor) + checkExpectDeclarationModifiers(classOrObject, classDescriptor) } private fun checkLocalAnnotation(classDescriptor: ClassDescriptor, classOrObject: KtClassOrObject) { @@ -618,14 +618,29 @@ class DeclarationsChecker( shadowedExtensionChecker.checkDeclaration(property, propertyDescriptor) checkPropertyTypeParametersAreUsedInReceiverType(propertyDescriptor) checkImplicitCallableType(property, propertyDescriptor) - checkPrivateExpectedDeclaration(property, propertyDescriptor) + checkExpectDeclarationModifiers(property, propertyDescriptor) checkBackingField(property) } - private fun checkPrivateExpectedDeclaration(declaration: KtDeclaration, descriptor: MemberDescriptor) { - if (descriptor.isExpect && DescriptorVisibilities.isPrivate(descriptor.visibility)) { + private fun checkExpectDeclarationModifiers(declaration: KtDeclaration, descriptor: MemberDescriptor) { + if (!descriptor.isExpect) return + + if (DescriptorVisibilities.isPrivate(descriptor.visibility)) { trace.report(EXPECTED_PRIVATE_DECLARATION.on(declaration.modifierList?.getModifier(KtTokens.PRIVATE_KEYWORD) ?: declaration)) } + + checkExpectDeclarationHasNoExternalModifier(declaration) + if (declaration is KtFunction) { + declaration.modifierList?.getModifier(KtTokens.TAILREC_KEYWORD)?.let { + trace.report(EXPECTED_TAILREC_FUNCTION.on(it)) + } + } + } + + private fun checkExpectDeclarationHasNoExternalModifier(declaration: KtDeclaration) { + declaration.modifierList?.getModifier(KtTokens.EXTERNAL_KEYWORD)?.let { + trace.report(EXPECTED_EXTERNAL_DECLARATION.on(it)) + } } private fun checkPropertyTypeParametersAreUsedInReceiverType(descriptor: PropertyDescriptor) { @@ -925,7 +940,7 @@ class DeclarationsChecker( trace.report(EXPECTED_DECLARATION_WITH_BODY.on(function)) } - checkPrivateExpectedDeclaration(function, functionDescriptor) + checkExpectDeclarationModifiers(function, functionDescriptor) } private fun checkActualFunction(element: KtDeclaration, functionDescriptor: FunctionDescriptor) { @@ -1022,6 +1037,9 @@ class DeclarationsChecker( } } } + if (propertyDescriptor.isExpect) { + checkExpectDeclarationHasNoExternalModifier(accessor) + } } private fun checkEnumEntry(enumEntry: KtEnumEntry, enumEntryClass: ClassDescriptor) { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt index 52da60b5644..f35c5403d5f 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt @@ -432,11 +432,9 @@ object AbstractExpectActualCompatibilityChecker { } if ( - expectFunction.isExternal && !actualFunction.isExternal || expectFunction.isInfix && !actualFunction.isInfix || expectFunction.isInline && !actualFunction.isInline || - expectFunction.isOperator && !actualFunction.isOperator || - expectFunction.isTailrec && !actualFunction.isTailrec + expectFunction.isOperator && !actualFunction.isOperator ) { return Incompatible.FunctionModifiersNotSubset } diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt new file mode 100644 index 00000000000..d94c059c7c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt @@ -0,0 +1,38 @@ +// MODULE: m1-common +// FILE: common.kt + +expect external fun foo() +expect fun bar() + +expect external var prop: String + +expect var getAndSet: String + external get + external set + +external expect val explicitGetter: String + external get + +expect external class A { + external fun foo() + fun bar() +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual external fun foo() +actual external fun bar() + +actual external var prop: String + +actual var getAndSet: String + external get + external set + +actual external val explicitGetter: String + external get + +actual class A { + actual external fun foo() + actual external fun bar() +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt new file mode 100644 index 00000000000..11aa061a3d7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt @@ -0,0 +1,38 @@ +// MODULE: m1-common +// FILE: common.kt + +expect external fun foo() +expect fun bar() + +expect external var prop: String + +expect var getAndSet: String + external get + external set + +external expect val explicitGetter: String + external get + +expect external class A { + external fun foo() + fun bar() +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual external fun foo() +actual external fun bar() + +actual external var prop: String + +actual var getAndSet: String + external get + external set + +actual external val explicitGetter: String + external get + +actual class A { + actual external fun foo() + actual external fun bar() +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt new file mode 100644 index 00000000000..303e1acae44 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt @@ -0,0 +1,24 @@ +// MODULE: m1-common +// FILE: common.kt + +expect tailrec fun foo(p: Int): Int +expect fun bar(p: Int): Int + +expect tailrec val notReport: String + +expect class A { + tailrec fun foo(p: Int): Int + fun bar(p: Int): Int +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual tailrec fun foo(p: Int): Int = foo(p) +actual tailrec fun bar(p: Int): Int = bar(p) + +actual val notReport: String = "123" + +actual class A { + actual tailrec fun foo(p: Int): Int = foo(p) + actual tailrec fun bar(p: Int): Int = bar(p) +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt new file mode 100644 index 00000000000..4d30aadaf4f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt @@ -0,0 +1,24 @@ +// MODULE: m1-common +// FILE: common.kt + +expect tailrec fun foo(p: Int): Int +expect fun bar(p: Int): Int + +expect tailrec val notReport: String + +expect class A { + tailrec fun foo(p: Int): Int + fun bar(p: Int): Int +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual tailrec fun foo(p: Int): Int = foo(p) +actual tailrec fun bar(p: Int): Int = bar(p) + +actual val notReport: String = "123" + +actual class A { + actual tailrec fun foo(p: Int): Int = foo(p) + actual tailrec fun bar(p: Int): Int = bar(p) +} diff --git a/compiler/testData/multiplatform/incompatibleFunctions/common.kt b/compiler/testData/multiplatform/incompatibleFunctions/common.kt index 6cf28f9c0eb..8508ef2bfd8 100644 --- a/compiler/testData/multiplatform/incompatibleFunctions/common.kt +++ b/compiler/testData/multiplatform/incompatibleFunctions/common.kt @@ -2,8 +2,4 @@ expect infix fun Int.plus(s: CharSequence): Int expect operator fun Double.times(x: CharArray) -expect external fun f1() - -expect inline fun f2() - -expect tailrec fun f3() +expect inline fun f1() diff --git a/compiler/testData/multiplatform/incompatibleFunctions/jvm.kt b/compiler/testData/multiplatform/incompatibleFunctions/jvm.kt index f4f11733cc8..88b4c270e17 100644 --- a/compiler/testData/multiplatform/incompatibleFunctions/jvm.kt +++ b/compiler/testData/multiplatform/incompatibleFunctions/jvm.kt @@ -3,7 +3,3 @@ actual fun Int.plus(s: CharSequence): Int = 0 actual fun Double.times(x: CharArray) {} actual fun f1() {} - -actual fun f2() {} - -actual fun f3() {} diff --git a/compiler/testData/multiplatform/incompatibleFunctions/output.txt b/compiler/testData/multiplatform/incompatibleFunctions/output.txt index 45b06596c96..64c6d021dcc 100644 --- a/compiler/testData/multiplatform/incompatibleFunctions/output.txt +++ b/compiler/testData/multiplatform/incompatibleFunctions/output.txt @@ -6,32 +6,20 @@ Output: Exit code: COMPILATION_ERROR Output: compiler/testData/multiplatform/incompatibleFunctions/jvm.kt:1:1: error: actual function 'plus' has no corresponding expected declaration -The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec): +The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (infix, inline, operator): public expect infix fun Int.plus(s: CharSequence): Int actual fun Int.plus(s: CharSequence): Int = 0 ^ compiler/testData/multiplatform/incompatibleFunctions/jvm.kt:3:1: error: actual function 'times' has no corresponding expected declaration -The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec): +The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (infix, inline, operator): public expect operator fun Double.times(x: CharArray): Unit actual fun Double.times(x: CharArray) {} ^ compiler/testData/multiplatform/incompatibleFunctions/jvm.kt:5:1: error: actual function 'f1' has no corresponding expected declaration -The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec): - public external expect fun f1(): Unit +The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (infix, inline, operator): + public expect inline fun f1(): Unit actual fun f1() {} ^ -compiler/testData/multiplatform/incompatibleFunctions/jvm.kt:7:1: error: actual function 'f2' has no corresponding expected declaration -The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec): - public expect inline fun f2(): Unit - -actual fun f2() {} -^ -compiler/testData/multiplatform/incompatibleFunctions/jvm.kt:9:1: error: actual function 'f3' has no corresponding expected declaration -The following declaration is incompatible because some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec): - public expect tailrec fun f3(): Unit - -actual fun f3() {} -^ diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 1c085239a6f..9b8ecc5a51c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -22252,6 +22252,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/expectDataObject.kt"); } + @Test + @TestMetadata("expectExternal.kt") + public void testExpectExternal() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectExternal.kt"); + } + @Test @TestMetadata("expectInterfaceApplicability.kt") public void testExpectInterfaceApplicability() throws Exception { @@ -22264,6 +22270,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt"); } + @Test + @TestMetadata("expectTailrec.kt") + public void testExpectTailrec() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/expectTailrec.kt"); + } + @Test @TestMetadata("headerFunInNonHeaderClass.kt") public void testHeaderFunInNonHeaderClass() throws Exception { diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt index 9a4f7fb6524..637aced0cfb 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -50,7 +50,7 @@ sealed class ExpectActualCompatibility { object FunctionModifiersDifferent : Incompatible("modifiers are different (suspend)") object FunctionModifiersNotSubset : Incompatible( - "some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec)" + "some modifiers on expected declaration are missing on the actual one (infix, inline, operator)" ) // Properties