[FE] Convert specific diagnostic for actual function with default arguments into a common incompatibility

^KT-59665 Fixed
Review: https://jetbrains.team/p/kt/reviews/11039/timeline

It's better to have this logic in common place
(AbstractExpectActualCompatibilityChecker) to avoid missing compilation
errors in the future

This commit fixes:
1. Missing compilation error for actual function with default arguments
   for 'actual typealias' KT-59665
2. Missing compilation error for actual function with default arguments
   for actual fake-override KT-59665

Alternative solution for KT-59665 is to create a special checker.

"incompatibility" vs "special checker":

Arguments for common incompatibility:
- What if we had a rule that expect and actual default params must
  match? If so then it certainly would be an incompatibility.
- Technically, we do the matching of expect and actual params (because
  we allow default params in common ancestors of expect and actual
  declarations).
- It's hard to check that the actual definition doesn't use default
  params because `ExpectedActualResolver.findActualForExpected` filters
  out fake-overrides and doesn't return them. It's not clear logic for
  me, that I'm afraid to touch.
  implicitActualFakeOverride_AbstractMap.kt test breaks if you drop this
  weird logic
- WEAK incompatibilities can be considered as "checkers". So it doesn't
  matter how it's implemented, as a "incompatibility" or a "checker"

Arguments against common incompatibility:
- Although we match expect and actual declarations to allow default
  params in common ancestors of expect and actual declarations, it's
  still can be considered that we check that the actual declaration
  doesn't have default params. And it doesn't feel right that we check
  correctness of the actual declaration in expect-actual matcher.
- ~~It may change the rules of expect actual matching~~ (It's not true,
  because ActualFunctionWithDefaultParameters is declared as WEAK
  incompatibility)
This commit is contained in:
Nikita Bobko
2023-06-28 16:42:26 +02:00
committed by teamcity
parent ab8913dee8
commit d39755b578
41 changed files with 450 additions and 59 deletions
@@ -2459,7 +2459,7 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ActualTypeAliasToNothing::class
}
interface ActualFunctionWithDefaultArguments : KtFirDiagnostic<PsiElement> {
interface ActualFunctionWithDefaultArguments : KtFirDiagnostic<KtFunction> {
override val diagnosticClass get() = ActualFunctionWithDefaultArguments::class
}
@@ -2968,7 +2968,7 @@ internal class ActualTypeAliasToNothingImpl(
internal class ActualFunctionWithDefaultArgumentsImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ActualFunctionWithDefaultArguments
) : KtAbstractFirDiagnostic<KtFunction>(firDiagnostic, token), KtFirDiagnostic.ActualFunctionWithDefaultArguments
internal class DefaultArgumentsInExpectWithActualTypealiasImpl(
override val expectClassSymbol: KtClassLikeSymbol,
@@ -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 {
@@ -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 {
@@ -1184,7 +1184,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_NOTHING by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error<PsiElement>()
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error<KtFunction>(PositioningStrategy.PARAMETERS_WITH_DEFAULT_VALUE)
val DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS by error<KtTypeAlias> {
parameter<FirClassSymbol<*>>("expectClassSymbol")
parameter<Collection<FirCallableSymbol<*>>>("members")
@@ -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,
@@ -635,7 +635,7 @@ object FirErrors {
val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_NOTHING by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0<PsiElement>()
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0<KtFunction>(SourceElementPositioningStrategies.PARAMETERS_WITH_DEFAULT_VALUE)
val DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS by error2<KtTypeAlias, FirClassSymbol<*>, Collection<FirCallableSymbol<*>>>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, FirVariableSymbol<*>>()
val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0<PsiElement>()
@@ -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
@@ -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,
@@ -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<TypeParameterSymbolMarker>
get() = asSymbol().typeParameterSymbols
override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker> {
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<ValueParameterSymbolMarker>
get() = asSymbol().valueParameterSymbols
@@ -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<LighterASTNode>
): List<TextRange> {
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<LighterASTNode>.primaryConstructor(nod
private fun FlyweightCapableTreeStructure<LighterASTNode>.valueParameterList(node: LighterASTNode): LighterASTNode? =
findChildByType(node, KtNodeTypes.VALUE_PARAMETER_LIST)
private fun FlyweightCapableTreeStructure<LighterASTNode>.valueParameters(node: LighterASTNode): List<LighterASTNode> =
valueParameterList(node)?.let { findChildrenByType(it, KtNodeTypes.VALUE_PARAMETER) }.orEmpty()
private fun FlyweightCapableTreeStructure<LighterASTNode>.typeReference(node: LighterASTNode): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef)
@@ -1490,6 +1508,12 @@ fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterA
return childrenRef.get()?.firstOrNull { it?.tokenType == type }
}
fun FlyweightCapableTreeStructure<LighterASTNode>.findChildrenByType(node: LighterASTNode, type: IElementType): List<LighterASTNode> {
val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef)
return childrenRef.get()?.filter { it?.tokenType == type }?.filterNotNull().orEmpty()
}
fun FlyweightCapableTreeStructure<LighterASTNode>.findLastChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef)
@@ -510,6 +510,15 @@ object PositioningStrategies {
}
}
@JvmField
val PARAMETERS_WITH_DEFAULT_VALUE: PositioningStrategy<KtFunction> = object : PositioningStrategy<KtFunction>() {
override fun mark(element: KtFunction): List<TextRange> =
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<KtParameter> = object : PositioningStrategy<KtParameter>() {
override fun mark(element: KtParameter): List<TextRange> {
@@ -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
@@ -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
@@ -811,7 +811,7 @@ public interface Errors {
DiagnosticFactory0<KtTypeAlias> ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtTypeAlias> ACTUAL_TYPE_ALIAS_TO_NULLABLE_TYPE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtTypeAlias> ACTUAL_TYPE_ALIAS_TO_NOTHING = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<PsiElement> ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtFunction> ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(ERROR, PARAMETERS_WITH_DEFAULT_VALUE);
DiagnosticFactory2<KtNamedDeclaration, ClassDescriptor, Collection<FunctionDescriptor>> DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<PsiElement, ValueParameterDescriptor> ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE =
DiagnosticFactory1.create(ERROR);
@@ -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
@@ -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<Incompatible<MemberDescriptor>, Collection<MemberDescriptor>>
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) {
@@ -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 <R> CallableSymbolMarker.processIr(
onFunction: (IrFunction) -> R,
onProperty: (IrProperty) -> R,
@@ -285,6 +291,9 @@ internal abstract class IrExpectActualMatchingContext(
onEnumEntry = { emptyList() }
)
override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker> =
throw NotImplementedError("Not implemented because it's unused")
override val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
get() = asIr().valueParameters.map { it.symbol }
@@ -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
}
@@ -39,6 +39,9 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : 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<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
/**
* Returns all symbols that are overridden by [this] symbol
*/
fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker>
val CallableSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList()
@@ -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<MemberDescriptor>,
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<MemberDescriptor>,
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<CallableSymbolMarker> =
(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<ConstructorDescriptor>() ?: return false
@@ -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<ExpectActualCompatibility<MemberDescriptor>, List<MemberDescriptor>>? {
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<CallableMemberDescriptor>().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<MemberDescriptor> =
// ?.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
@@ -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")
@@ -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")
@@ -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 <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>A<!> : FooProvider
@@ -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
@@ -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
@@ -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 <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> : A
interface A : B<Int> {
override fun getDefault(): Int = 3
}
interface B<T> {
fun foo(param: T = getDefault()) {}
fun getDefault(): T
}
@@ -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) {}
}
@@ -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 <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> : A
interface A : B {
override fun foo(param: Int) {}
}
interface B {
fun foo(param: Int = 3) {}
}
@@ -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 <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> = FooImpl
class FooImpl {
fun foo(a: Int = 2) {
}
}
@@ -0,0 +1,13 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
public expect fun <T : Comparable<T>> Array<out T>.sort(fromIndex: Int = 0, toIndex: Int = size): Unit
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
public actual fun <T : Comparable<T>> Array<out T>.sort(<!ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS!>fromIndex: Int = 0<!>, <!ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS!>toIndex: Int = size<!>): Unit {
}
public fun <T> Array<out T>.sort(fromIndex: Int = 0, toIndex: Int = size): Unit {
}
@@ -0,0 +1,13 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
public expect fun <T : Comparable<T>> Array<out T>.sort(fromIndex: Int = 0, toIndex: Int = size): Unit
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
public actual fun <T : Comparable<T>> Array<out T>.sort(fromIndex: Int, toIndex: Int): Unit {
}
public fun <T> Array<out T>.sort(fromIndex: Int = 0, toIndex: Int = size): Unit {
}
@@ -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)
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias DefaultArgsInConstructor = DefaultArgsInConstructorImpl<!>
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>DefaultArgsInConstructor<!> = DefaultArgsInConstructorImpl<!>
@@ -28,13 +28,13 @@ class AImpl {
fun foo(p1: String = "impl", p2: String = "impl", p3: String) {}
}
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias A = AImpl<!>
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>A<!> = AImpl<!>
class BImpl {
fun foo(s: String = "impl") {}
}
actual typealias B = BImpl
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>B<!> = BImpl
class WithDefaultArgFromSuperImpl : I {
override fun methodWithDefaultArg(s: String) {}
@@ -28,13 +28,13 @@ class AImpl {
fun foo(p1: String = "impl", p2: String = "impl", p3: String) {}
}
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias A = AImpl<!>
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>A<!> = AImpl<!>
class BImpl {
fun foo(s: String = "impl") {}
}
actual typealias B = BImpl
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>B<!> = BImpl
class WithDefaultArgFromSuperImpl : I {
override fun methodWithDefaultArg(s: String) {}
@@ -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 {
@@ -55,6 +55,8 @@ sealed class ExpectActualCompatibility<out D> {
object FunctionModifiersNotSubset : Incompatible<Nothing>(
"some modifiers on expected declaration are missing on the actual one (infix, inline, operator)"
)
object ActualFunctionWithDefaultParameters :
Incompatible<Nothing>("actual function cannot have default argument values, they should be declared in the expected function")
// Properties
@@ -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<RegexOption>) {
/** Creates a regular expression from the specified [pattern] string and the specified single [option]. */
@@ -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 {
@@ -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 {