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 423a7307a84..0c5de6e4fba 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 @@ -2459,7 +2459,7 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ActualTypeAliasToNothing::class } - interface ActualFunctionWithDefaultArguments : KtFirDiagnostic { + interface ActualFunctionWithDefaultArguments : KtFirDiagnostic { override val diagnosticClass get() = ActualFunctionWithDefaultArguments::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 dd9e3218082..d3dc377d163 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 @@ -2968,7 +2968,7 @@ internal class ActualTypeAliasToNothingImpl( internal class ActualFunctionWithDefaultArgumentsImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, -) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.ActualFunctionWithDefaultArguments +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.ActualFunctionWithDefaultArguments internal class DefaultArgumentsInExpectWithActualTypealiasImpl( override val expectClassSymbol: KtClassLikeSymbol, 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 d0c180a7ddc..d701ec40417 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 @@ -32,17 +32,71 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverrideVirtual.kt"); } + @Test + @TestMetadata("actualFakeOverride_defaultParamsIncompatibility.kt") + public void testActualFakeOverride_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_substitutionFakeOverride.kt") + public void testActualFakeOverride_substitutionFakeOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_compatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_compatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_incompatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_incompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt"); + } + @Test @TestMetadata("actualTypealiasToSpecialAnnotation.kt") public void testActualTypealiasToSpecialAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealiasToSpecialAnnotation.kt"); } + @Test + @TestMetadata("actualTypealias_defaultParamsIncompatibility.kt") + public void testActualTypealias_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt"); + } + @Test public void testAllFilesPresentInMultiplatform() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("arraySort.kt") + public void testArraySort() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySort.kt"); + } + + @Test + @TestMetadata("arraySortFixed.kt") + public void testArraySortFixed() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() 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 8302879d1c2..062083fa793 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 @@ -32,17 +32,71 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverrideVirtual.kt"); } + @Test + @TestMetadata("actualFakeOverride_defaultParamsIncompatibility.kt") + public void testActualFakeOverride_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_substitutionFakeOverride.kt") + public void testActualFakeOverride_substitutionFakeOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_compatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_compatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_incompatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_incompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt"); + } + @Test @TestMetadata("actualTypealiasToSpecialAnnotation.kt") public void testActualTypealiasToSpecialAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealiasToSpecialAnnotation.kt"); } + @Test + @TestMetadata("actualTypealias_defaultParamsIncompatibility.kt") + public void testActualTypealias_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt"); + } + @Test public void testAllFilesPresentInMultiplatform() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("arraySort.kt") + public void testArraySort() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySort.kt"); + } + + @Test + @TestMetadata("arraySortFixed.kt") + public void testArraySortFixed() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() 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 1b4fe5a3cbe..afe990fb787 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 @@ -1184,7 +1184,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error(PositioningStrategy.DECLARATION_SIGNATURE) val ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE by error(PositioningStrategy.DECLARATION_SIGNATURE) val ACTUAL_TYPE_ALIAS_TO_NOTHING by error(PositioningStrategy.DECLARATION_SIGNATURE) - val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error() + val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error(PositioningStrategy.PARAMETERS_WITH_DEFAULT_VALUE) val DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS by error { parameter>("expectClassSymbol") parameter>>("members") 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 e7713d947c6..f27155a6c6c 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 @@ -52,6 +52,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { MODALITY_MODIFIER, OPERATOR, PARAMETER_DEFAULT_VALUE, + PARAMETERS_WITH_DEFAULT_VALUE, PARAMETER_VARARG_MODIFIER, DECLARATION_RETURN_TYPE, OVERRIDE_MODIFIER, 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 fc2236ab663..04ac8d125b8 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 @@ -635,7 +635,7 @@ object FirErrors { val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val ACTUAL_TYPE_ALIAS_TO_NOTHING by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) - val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0() + val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0(SourceElementPositioningStrategies.PARAMETERS_WITH_DEFAULT_VALUE) val DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS by error2, Collection>>() val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1>() val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualCallableDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualCallableDeclarationChecker.kt index c9f664ed417..32dc97090a1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualCallableDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualCallableDeclarationChecker.kt @@ -22,21 +22,9 @@ object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() { override fun check(declaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (!declaration.isActual) return - if (declaration is FirFunction) { - checkActualFunctionWithDefaultArguments(declaration, reporter, context) - } checkReturnTypes(declaration, context, reporter) } - private fun checkActualFunctionWithDefaultArguments(function: FirFunction, reporter: DiagnosticReporter, context: CheckerContext) { - if (function.symbol.isAnnotationConstructor(context.session)) return - for (valueParameter in function.valueParameters) { - if (valueParameter.defaultValue != null) { - reporter.reportOn(valueParameter.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context) - } - } - } - private fun checkReturnTypes(callableDeclaration: FirCallableDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { val actualFunctionSymbol = callableDeclaration.symbol val expectFunctionSymbol = actualFunctionSymbol.getSingleExpectForActualOrNull() as? FirCallableSymbol ?: return 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 72c9fe9d9f9..209dcdaf03f 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 @@ -6,6 +6,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 import org.jetbrains.kotlin.diagnostics.DiagnosticReporter @@ -168,7 +169,10 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { } Compatible !in compatibilityToMembersMap -> { - if (requireActualModifier(declaration.symbol, context.session)) { + // A nicer diagnostic for functions with default params + if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is Incompatible.ActualFunctionWithDefaultParameters }) { + reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context) + } else if (requireActualModifier(declaration.symbol, context.session)) { reporter.reportOn( source, FirErrors.ACTUAL_WITHOUT_EXPECT, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index 0b7adc18ed3..485004ef7b1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt @@ -8,15 +8,13 @@ package org.jetbrains.kotlin.fir.resolve.transformers.mpp import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibility -import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext -import org.jetbrains.kotlin.fir.FirExpectActualMatchingContextFactory -import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.getRetention import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride +import org.jetbrains.kotlin.fir.declarations.utils.isOverride import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.scopes.* @@ -47,6 +45,9 @@ class FirExpectActualMatchingContextImpl private constructor( override val shouldCheckReturnTypesOfCallables: Boolean get() = false + override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + get() = true + override val enumConstructorsAreAlwaysCompatible: Boolean get() = true @@ -243,6 +244,23 @@ class FirExpectActualMatchingContextImpl private constructor( get() = asSymbol().resolvedReturnType.type override val CallableSymbolMarker.typeParameters: List get() = asSymbol().typeParameterSymbols + + override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence { + return when (val symbol = asSymbol()) { + is FirConstructorSymbol, is FirFunctionWithoutNameSymbol -> sequenceOf(this) + is FirNamedFunctionSymbol -> { + val session = symbol.moduleData.session + val containingClass = symbol.containingClassLookupTag()?.toFirRegularClassSymbol(session) + ?: return sequenceOf(symbol) + (sequenceOf(symbol) + symbol.overriddenFunctions(containingClass, session, scopeSession).asSequence()) + // Tests work even if you don't filter out fake-overrides. Filtering fake-overrides is needed because + // the returned descriptors are compared by `equals`. And `equals` for fake-overrides is weird. + // I didn't manage to invent a test that would check this condition + .filter { !it.isSubstitutionOrIntersectionOverride } + } + } + } + override val FunctionSymbolMarker.valueParameters: List get() = asSymbol().valueParameterSymbols 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 035b21709e9..cc51a5f2184 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 @@ -533,6 +533,21 @@ object LightTreePositioningStrategies { } } + val PARAMETERS_WITH_DEFAULT_VALUE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + val newNodes = tree.valueParameters(node).filter { tree.defaultValue(it) != null }.takeIf(List<*>::isNotEmpty) + ?: tree.valueParameterList(node)?.let(::listOf) + ?: tree.nameIdentifier(node)?.let(::listOf) + ?: listOf(node) + return newNodes.flatMap { markElement(it, startOffset, endOffset, tree, node) } + } + } + val PARAMETER_VARARG_MODIFIER: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { override fun mark( node: LighterASTNode, @@ -1401,6 +1416,9 @@ private fun FlyweightCapableTreeStructure.primaryConstructor(nod private fun FlyweightCapableTreeStructure.valueParameterList(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtNodeTypes.VALUE_PARAMETER_LIST) +private fun FlyweightCapableTreeStructure.valueParameters(node: LighterASTNode): List = + valueParameterList(node)?.let { findChildrenByType(it, KtNodeTypes.VALUE_PARAMETER) }.orEmpty() + private fun FlyweightCapableTreeStructure.typeReference(node: LighterASTNode): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) @@ -1490,6 +1508,12 @@ fun FlyweightCapableTreeStructure.findChildByType(node: LighterA return childrenRef.get()?.firstOrNull { it?.tokenType == type } } +fun FlyweightCapableTreeStructure.findChildrenByType(node: LighterASTNode, type: IElementType): List { + val childrenRef = Ref>() + getChildren(node, childrenRef) + return childrenRef.get()?.filter { it?.tokenType == type }?.filterNotNull().orEmpty() +} + fun FlyweightCapableTreeStructure.findLastChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) 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 991ec4fbc0c..bd891ebc020 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 @@ -510,6 +510,15 @@ object PositioningStrategies { } } + @JvmField + val PARAMETERS_WITH_DEFAULT_VALUE: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: KtFunction): List = + element.valueParameters.filter(KtParameter::hasDefaultValue).takeIf(List<*>::isNotEmpty)?.flatMap { markNode(it.node) } + ?: element.valueParameterList?.let { markNode(it.node) } + ?: element.nameIdentifier?.let { markNode(it.node) } + ?: markNode(element.node) + } + @JvmField val PARAMETER_VARARG_MODIFIER: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtParameter): 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 a41fe84e286..79f0ac553e9 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 @@ -148,6 +148,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.PARAMETER_DEFAULT_VALUE ) + val PARAMETERS_WITH_DEFAULT_VALUE = SourceElementPositioningStrategy( + LightTreePositioningStrategies.PARAMETERS_WITH_DEFAULT_VALUE, + PositioningStrategies.PARAMETERS_WITH_DEFAULT_VALUE + ) + val PARAMETER_VARARG_MODIFIER = SourceElementPositioningStrategy( LightTreePositioningStrategies.PARAMETER_VARARG_MODIFIER, PositioningStrategies.PARAMETER_VARARG_MODIFIER diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt index 54b00894f8f..0e82e3c391b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt @@ -79,6 +79,7 @@ object ClassicPositioningStrategies { } ExpectActualCompatibility.Incompatible.ParameterCount, ExpectActualCompatibility.Incompatible.ParameterTypes, ExpectActualCompatibility.Incompatible.ParameterNames, ExpectActualCompatibility.Incompatible.ValueParameterVararg, + ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters, ExpectActualCompatibility.Incompatible.ValueParameterNoinline, ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> { callableDeclaration?.valueParameterList diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7ba774eaedf..1bafa971057 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -811,7 +811,7 @@ public interface Errors { DiagnosticFactory0 ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 ACTUAL_TYPE_ALIAS_TO_NOTHING = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); - DiagnosticFactory0 ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(ERROR, PARAMETERS_WITH_DEFAULT_VALUE); DiagnosticFactory2> DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index b92395f4177..5f5abdeecfa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -261,10 +261,6 @@ class DeclarationsChecker( checkVarargParameters(trace, constructorDescriptor) checkConstructorVisibility(constructorDescriptor, declaration) checkExpectedClassConstructor(constructorDescriptor, declaration) - - if (constructorDescriptor.isActual) { - checkActualFunction(declaration, constructorDescriptor) - } } private fun checkExpectedClassConstructor(constructorDescriptor: ClassConstructorDescriptor, declaration: KtConstructor<*>) { @@ -938,9 +934,6 @@ class DeclarationsChecker( if (functionDescriptor.isExpect) { checkExpectedFunction(function, functionDescriptor) } - if (functionDescriptor.isActual) { - checkActualFunction(function, functionDescriptor) - } shadowedExtensionChecker.checkDeclaration(function, functionDescriptor) } @@ -953,22 +946,6 @@ class DeclarationsChecker( checkExpectDeclarationModifiers(function, functionDescriptor) } - private fun checkActualFunction(element: KtDeclaration, functionDescriptor: FunctionDescriptor) { - // Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation - // is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors - if (!functionDescriptor.isAnnotationConstructor()) { - for (valueParameter in functionDescriptor.valueParameters) { - if (valueParameter.declaresDefaultValue()) { - trace.report( - ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS.on( - DescriptorToSourceUtils.descriptorToDeclaration(valueParameter) ?: element - ) - ) - } - } - } - } - private fun checkImplicitCallableType(declaration: KtCallableDeclaration, descriptor: CallableDescriptor) { descriptor.returnType?.unwrap()?.let { val target = declaration.nameIdentifier ?: declaration diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt index 6fe90bc1846..2290d658d4e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt @@ -297,7 +297,8 @@ class ExpectedActualDeclarationChecker( trace: BindingTrace, moduleVisibilityFilter: ModuleFilter ) { - val compatibility = ExpectedActualResolver.findExpectedForActual(descriptor, moduleVisibilityFilter) + val compatibility = ExpectedActualResolver + .findExpectedForActual(descriptor, moduleVisibilityFilter, shouldCheckAbsenceOfDefaultParamsInActual = true) ?: return checkAmbiguousExpects(compatibility, trace, reportOn, descriptor) @@ -357,7 +358,12 @@ class ExpectedActualDeclarationChecker( assert(compatibility.keys.all { it is Incompatible }) @Suppress("UNCHECKED_CAST") val incompatibility = compatibility as Map, Collection> - trace.report(Errors.ACTUAL_WITHOUT_EXPECT.on(reportOn, descriptor, incompatibility)) + // A nicer diagnostic for functions with default params + if (reportOn is KtFunction && incompatibility.keys.any { it is Incompatible.ActualFunctionWithDefaultParameters }) { + trace.report(Errors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS.on(reportOn)) + } else { + trace.report(Errors.ACTUAL_WITHOUT_EXPECT.on(reportOn, descriptor, incompatibility)) + } } else { val expected = compatibility[Compatible]!!.first() if (expected is ClassDescriptor && expected.kind == ClassKind.ANNOTATION_CLASS) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt index 8ac7f101622..d14313fdeaa 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt @@ -49,6 +49,12 @@ internal abstract class IrExpectActualMatchingContext( override val allowTransitiveSupertypesActualization: Boolean get() = true + // This incompatibility is often suppressed in the source code (e.g. in kotlin-stdlib). + // The backend must be able to do expect-actual matching to emit bytecode + // That's why we disable the checker here. Probably, this checker can be enabled once KT-60426 is fixed + override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + get() = false + private inline fun CallableSymbolMarker.processIr( onFunction: (IrFunction) -> R, onProperty: (IrProperty) -> R, @@ -285,6 +291,9 @@ internal abstract class IrExpectActualMatchingContext( onEnumEntry = { emptyList() } ) + override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence = + throw NotImplementedError("Not implemented because it's unused") + override val FunctionSymbolMarker.valueParameters: List get() = asIr().valueParameters.map { it.symbol } 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 1a00e87e4a1..f1a9e869e45 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 @@ -376,6 +376,22 @@ object AbstractExpectActualCompatibilityChecker { } } + if (shouldCheckAbsenceOfDefaultParamsInActual) { + // "Default parameters in actual" check is required only for functions, because only functions can have parameters + if (actualDeclaration is FunctionSymbolMarker && expectDeclaration is FunctionSymbolMarker) { + // Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation + // is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors + if (!actualDeclaration.isAnnotationConstructor() && + // If default params came from common supertypes of actual class and expect class then it's a valid code. + // Here we filter out such default params. + (actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet()) + .flatMap { it.valueParameters }.any { it.hasDefaultValue } + ) { + return Incompatible.ActualFunctionWithDefaultParameters + } + } + } + if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) { return Incompatible.ValueParameterVararg } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt index 6511221d01c..9935d2e57eb 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -39,6 +39,9 @@ interface ExpectActualMatchingContext : TypeSystemC val enumConstructorsAreAlwaysCompatible: Boolean get() = false + // Try to drop it once KT-61105 is fixed + val shouldCheckAbsenceOfDefaultParamsInActual: Boolean + /** * This flag determines, how visibilities for classes/typealiases will be matched * - `false` means that visibilities should be identical @@ -115,6 +118,11 @@ interface ExpectActualMatchingContext : TypeSystemC val CallableSymbolMarker.typeParameters: List val FunctionSymbolMarker.valueParameters: List + /** + * Returns all symbols that are overridden by [this] symbol + */ + fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence + val CallableSymbolMarker.valueParameters: List get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList() diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt index f66fdfc15a5..815ae891b53 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -34,9 +35,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.castAll import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled import org.jetbrains.kotlin.utils.keysToMap -class ClassicExpectActualMatchingContext(val platformModule: ModuleDescriptor) : ExpectActualMatchingContext, - TypeSystemContext by ClassicTypeSystemContextForCS(platformModule.builtIns, KotlinTypeRefiner.Default) -{ +class ClassicExpectActualMatchingContext( + val platformModule: ModuleDescriptor, + /** + * You want to enable this check only in expect-actual matcher checker. And disable everywhere else (especially on backends) + * + * Otherwise, it won't be possible to suppress the compilation error in user code + */ + override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean = false +) : ExpectActualMatchingContext, + TypeSystemContext by ClassicTypeSystemContextForCS(platformModule.builtIns, KotlinTypeRefiner.Default) { override val shouldCheckReturnTypesOfCallables: Boolean get() = true @@ -194,6 +202,12 @@ class ClassicExpectActualMatchingContext(val platformModule: ModuleDescriptor) : get() = asDescriptor().isCrossinline override val ValueParameterSymbolMarker.hasDefaultValue: Boolean get() = asDescriptor().declaresDefaultValue() + override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence = + (sequenceOf(asDescriptor()) + asDescriptor().overriddenTreeAsSequence(useOriginal = true)) + // Tests work even if you don't filter out fake-overrides. Filtering fake-overrides is needed because + // the returned descriptors are compared by `equals`. And `equals` for fake-overrides is weird. + // I didn't manage to invent a test that would check this condition + .filter { it.kind.isReal } override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean { val descriptor = safeAsDescriptor() ?: return false diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt index 6a6712f01ea..d72f1c5d716 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt @@ -56,9 +56,10 @@ object ExpectedActualResolver { fun findExpectedForActual( actual: MemberDescriptor, - moduleFilter: (ModuleDescriptor) -> Boolean = allModulesProvidingExpectsFor(actual.module) + moduleFilter: (ModuleDescriptor) -> Boolean = allModulesProvidingExpectsFor(actual.module), + shouldCheckAbsenceOfDefaultParamsInActual: Boolean = false, ): Map, List>? { - val context = ClassicExpectActualMatchingContext(actual.module) + val context = ClassicExpectActualMatchingContext(actual.module, shouldCheckAbsenceOfDefaultParamsInActual) return when (actual) { is CallableMemberDescriptor -> { val container = actual.containingDeclaration @@ -66,7 +67,8 @@ object ExpectedActualResolver { is ClassifierDescriptorWithTypeParameters -> { // TODO: replace with 'singleOrNull' as soon as multi-module diagnostic tests are refactored val expectedClass = - findExpectedForActual(container, moduleFilter)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor + findExpectedForActual(container, moduleFilter, shouldCheckAbsenceOfDefaultParamsInActual)?.values + ?.firstOrNull()?.firstOrNull() as? ClassDescriptor with(context) { expectedClass?.getMembersForExpectClass(actual.name)?.filterIsInstance().orEmpty() } @@ -160,11 +162,12 @@ object ExpectedActualResolver { } } -// FIXME(dsavvinov): review clients, as they won't work properly in HMPP projects +// FIXME(dsavvinov): review clients, as they won't work properly in HMPP projects. KT-61105 @JvmOverloads fun MemberDescriptor.findCompatibleActualsForExpected( platformModule: ModuleDescriptor, moduleFilter: ModuleFilter = allModulesProvidingActualsFor(module, platformModule) ): List = + // ?.get(Compatible) is suspicious. Probably, we must check not only Compatible but Incompatible.WeakIncompatible as well ExpectedActualResolver.findActualForExpected(this, platformModule, moduleFilter)?.get(Compatible).orEmpty() @JvmOverloads diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt index 3a7cab2e617..e47648ec2d3 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/bothInExpectAndActual2.kt @@ -13,6 +13,7 @@ expect interface J : I // FILE: platform.kt +@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Counterpart for @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") actual interface I { // This test should be updated once KT-22818 is fixed; default values are not allowed in the actual function @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") diff --git a/compiler/testData/codegen/box/multiplatform/k2/defaultArguments/bothInExpectAndActual2.kt b/compiler/testData/codegen/box/multiplatform/k2/defaultArguments/bothInExpectAndActual2.kt index e8e549a1785..127a13480d6 100644 --- a/compiler/testData/codegen/box/multiplatform/k2/defaultArguments/bothInExpectAndActual2.kt +++ b/compiler/testData/codegen/box/multiplatform/k2/defaultArguments/bothInExpectAndActual2.kt @@ -13,6 +13,7 @@ expect interface J : I // MODULE: platform()()(common) // FILE: platform.kt +@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Counterpart for @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") actual interface I { // This test should be updated once KT-22818 is fixed; default values are not allowed in the actual function @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt new file mode 100644 index 00000000000..a0dd9bb0f13 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt @@ -0,0 +1,16 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt + +expect class A { + fun foo(a: Int): Int +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +interface FooProvider { + fun foo(a: Int = 2): Int = 42 +} + +actual class A : FooProvider diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt new file mode 100644 index 00000000000..102586f57f6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt @@ -0,0 +1,12 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +interface Shared { + fun sharedMethod(withDefaultParam: Int = 2) {} +} + +expect class Foo : Shared + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual class Foo : Shared diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt new file mode 100644 index 00000000000..0fa48b571e2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt @@ -0,0 +1,14 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +interface Shared { + fun sharedMethod(withDefaultParam: Int = 2) {} +} + +interface Transitive + +expect class Foo : Transitive + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual class Foo : Transitive diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt new file mode 100644 index 00000000000..81ada2a64d7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt @@ -0,0 +1,20 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(param: Int) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual class Foo : A + +interface A : B { + override fun getDefault(): Int = 3 +} + +interface B { + fun foo(param: T = getDefault()) {} + + fun getDefault(): T +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt new file mode 100644 index 00000000000..0b10759869e --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt @@ -0,0 +1,18 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(param: Int) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual class Foo : A + +interface A : B { + override fun foo(param: Int) {} +} + +interface B { + fun foo(param: Int) {} +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt new file mode 100644 index 00000000000..58d893c6d5b --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt @@ -0,0 +1,18 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(param: Int) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual class Foo : A + +interface A : B { + override fun foo(param: Int) {} +} + +interface B { + fun foo(param: Int = 3) {} +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt b/compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt new file mode 100644 index 00000000000..0f8ebb0ce9b --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt @@ -0,0 +1,15 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + fun foo(a: Int) +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual typealias Foo = FooImpl + +class FooImpl { + fun foo(a: Int = 2) { + } +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/arraySort.kt b/compiler/testData/diagnostics/tests/multiplatform/arraySort.kt new file mode 100644 index 00000000000..4417d5ae5ca --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/arraySort.kt @@ -0,0 +1,13 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt + +public expect fun > Array.sort(fromIndex: Int = 0, toIndex: Int = size): Unit + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +public actual fun > Array.sort(fromIndex: Int = 0, toIndex: Int = size): Unit { +} + +public fun Array.sort(fromIndex: Int = 0, toIndex: Int = size): Unit { +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt b/compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt new file mode 100644 index 00000000000..0d4d43f65ed --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt @@ -0,0 +1,13 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt + +public expect fun > Array.sort(fromIndex: Int = 0, toIndex: Int = size): Unit + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +public actual fun > Array.sort(fromIndex: Int, toIndex: Int): Unit { +} + +public fun Array.sort(fromIndex: Int = 0, toIndex: Int = size): Unit { +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/constructorDefaultArgsViaActualTypealias.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/constructorDefaultArgsViaActualTypealias.kt index aa1578c468b..375475c3b1f 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/constructorDefaultArgsViaActualTypealias.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/constructorDefaultArgsViaActualTypealias.kt @@ -7,4 +7,4 @@ expect class DefaultArgsInConstructor(p1: String = "common", p2: String = "commo // FILE: jvm.kt class DefaultArgsInConstructorImpl(p1: String = "common", p2: String = "common", p3: String) -actual typealias DefaultArgsInConstructor = DefaultArgsInConstructorImpl +actual typealias DefaultArgsInConstructor = DefaultArgsInConstructorImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt index f0471169b53..a1d13252c63 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.fir.kt @@ -28,13 +28,13 @@ class AImpl { fun foo(p1: String = "impl", p2: String = "impl", p3: String) {} } -actual typealias A = AImpl +actual typealias A = AImpl class BImpl { fun foo(s: String = "impl") {} } -actual typealias B = BImpl +actual typealias B = BImpl class WithDefaultArgFromSuperImpl : I { override fun methodWithDefaultArg(s: String) {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt index 2aff9ad6a38..5e46b410fcf 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualTypealias.kt @@ -28,13 +28,13 @@ class AImpl { fun foo(p1: String = "impl", p2: String = "impl", p3: String) {} } -actual typealias A = AImpl +actual typealias A = AImpl class BImpl { fun foo(s: String = "impl") {} } -actual typealias B = BImpl +actual typealias B = BImpl class WithDefaultArgFromSuperImpl : I { override fun methodWithDefaultArg(s: String) {} 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 8151a2f6a8c..9e311f34d97 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 @@ -22711,17 +22711,71 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverrideVirtual.kt"); } + @Test + @TestMetadata("actualFakeOverride_defaultParamsIncompatibility.kt") + public void testActualFakeOverride_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_defaultParamsIncompatibility.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt") + public void testActualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_paramsAreCompatibleViaTransitiveSharedMethodWithDefaultParams.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_substitutionFakeOverride.kt") + public void testActualFakeOverride_substitutionFakeOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_substitutionFakeOverride.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_compatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_compatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_compatible.kt"); + } + + @Test + @TestMetadata("actualFakeOverride_transitiveFakeOverrides_incompatible.kt") + public void testActualFakeOverride_transitiveFakeOverrides_incompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualFakeOverride_transitiveFakeOverrides_incompatible.kt"); + } + @Test @TestMetadata("actualTypealiasToSpecialAnnotation.kt") public void testActualTypealiasToSpecialAnnotation() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealiasToSpecialAnnotation.kt"); } + @Test + @TestMetadata("actualTypealias_defaultParamsIncompatibility.kt") + public void testActualTypealias_defaultParamsIncompatibility() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/actualTypealias_defaultParamsIncompatibility.kt"); + } + @Test public void testAllFilesPresentInMultiplatform() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("arraySort.kt") + public void testArraySort() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySort.kt"); + } + + @Test + @TestMetadata("arraySortFixed.kt") + public void testArraySortFixed() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() 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 45aabd08d35..5a4ed1a72e4 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 @@ -55,6 +55,8 @@ sealed class ExpectActualCompatibility { object FunctionModifiersNotSubset : Incompatible( "some modifiers on expected declaration are missing on the actual one (infix, inline, operator)" ) + object ActualFunctionWithDefaultParameters : + Incompatible("actual function cannot have default argument values, they should be declared in the expected function") // Properties diff --git a/libraries/stdlib/js/src/kotlin/text/regex.kt b/libraries/stdlib/js/src/kotlin/text/regex.kt index 2a089bfff1b..1c1da983cd7 100644 --- a/libraries/stdlib/js/src/kotlin/text/regex.kt +++ b/libraries/stdlib/js/src/kotlin/text/regex.kt @@ -61,6 +61,7 @@ public actual operator fun MatchGroupCollection.get(name: String): MatchGroup? { * * @constructor Creates a regular expression from the specified [pattern] string and the specified set of [options]. */ +@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Counterpart for @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual class Regex actual constructor(pattern: String, options: Set) { /** Creates a regular expression from the specified [pattern] string and the specified single [option]. */ diff --git a/libraries/stdlib/jvm/src/kotlin/text/regex/Regex.kt b/libraries/stdlib/jvm/src/kotlin/text/regex/Regex.kt index 087e338073b..bdc939036c6 100644 --- a/libraries/stdlib/jvm/src/kotlin/text/regex/Regex.kt +++ b/libraries/stdlib/jvm/src/kotlin/text/regex/Regex.kt @@ -78,6 +78,7 @@ public actual data class MatchGroup(public actual val value: String, public val * * For pattern syntax reference see [Pattern]. */ +@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Counterpart for @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual class Regex @PublishedApi internal constructor(private val nativePattern: Pattern) : Serializable { diff --git a/libraries/stdlib/native-wasm/src/kotlin/text/Regex.kt b/libraries/stdlib/native-wasm/src/kotlin/text/Regex.kt index d6509251700..bc651ae57b5 100644 --- a/libraries/stdlib/native-wasm/src/kotlin/text/Regex.kt +++ b/libraries/stdlib/native-wasm/src/kotlin/text/Regex.kt @@ -82,6 +82,7 @@ public actual operator fun MatchGroupCollection.get(name: String): MatchGroup? { * * Note that in the future, the behavior of regular expression matching and replacement functions can be altered to match JVM implementation behavior where differences exist. */ +@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // Counterpart for @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual class Regex internal constructor(internal val nativePattern: Pattern) { internal enum class Mode {