From 6e83820f97fbaeb8a2dd81180ecf7796701e4ff9 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 21 May 2021 15:14:03 +0300 Subject: [PATCH] Move ExpectActualCompatibility to :compiler:resolution.common --- .../jetbrains/kotlin/diagnostics/Errors.java | 8 +- .../diagnostics/PositioningStrategies.kt | 7 +- .../rendering/DefaultErrorMessages.java | 22 +++-- ...atformIncompatibilityDiagnosticRenderer.kt | 14 +-- .../ExpectedActualDeclarationChecker.kt | 27 +++--- .../ExpectActualCompatibility.kt | 90 +++++++++++++++++++ .../multiplatform/ExpectedActualResolver.kt | 90 +++---------------- .../kotlin/idea/util/expectActualUtil.kt | 5 +- .../OptionalExpectationInspection.kt | 7 +- 9 files changed, 153 insertions(+), 117 deletions(-) create mode 100644 compiler/resolution.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7fcad2ce48a..193b6ee2523 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible; +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible; import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData; import org.jetbrains.kotlin.types.KotlinType; @@ -704,10 +704,10 @@ public interface Errors { DiagnosticFactory0 EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND = DiagnosticFactory0.create(ERROR); DiagnosticFactory3>> NO_ACTUAL_FOR_EXPECT = + Map, Collection>> NO_ACTUAL_FOR_EXPECT = DiagnosticFactory3.create(ERROR, INCOMPATIBLE_DECLARATION); DiagnosticFactory2>> ACTUAL_WITHOUT_EXPECT = + Map, Collection>> ACTUAL_WITHOUT_EXPECT = DiagnosticFactory2.create(ERROR, INCOMPATIBLE_DECLARATION); DiagnosticFactory2> AMBIGUOUS_ACTUALS = DiagnosticFactory2.create(ERROR, INCOMPATIBLE_DECLARATION); @@ -715,7 +715,7 @@ public interface Errors { DiagnosticFactory2.create(ERROR, INCOMPATIBLE_DECLARATION); DiagnosticFactory2>>>> NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS = + List, Collection>>>> NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS = DiagnosticFactory2.create(ERROR, ACTUAL_DECLARATION_NAME); DiagnosticFactory0 ACTUAL_MISSING = DiagnosticFactory0.create(ERROR, ACTUAL_DECLARATION_NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index b4511f73c14..3056e6d57f7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -11,6 +11,7 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.cfg.UnreachableCode +import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.diagnostics.Errors.ACTUAL_WITHOUT_EXPECT import org.jetbrains.kotlin.diagnostics.Errors.NO_ACTUAL_FOR_EXPECT import org.jetbrains.kotlin.lexer.KtModifierKeywordToken @@ -19,8 +20,8 @@ import org.jetbrains.kotlin.lexer.KtTokens.MODALITY_MODIFIERS import org.jetbrains.kotlin.lexer.KtTokens.VISIBILITY_MODIFIERS import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible.* +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible.* import org.jetbrains.kotlin.utils.sure object PositioningStrategies { @@ -114,7 +115,7 @@ object PositioningStrategies { } } - private val ParametrizedDiagnostic.firstIncompatibility: Incompatible? + private val ParametrizedDiagnostic.firstIncompatibility: Incompatible? get() { val map = when (factory) { NO_ACTUAL_FOR_EXPECT -> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 46bbbd6af45..a194d8ad349 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -11,20 +11,21 @@ import kotlin.collections.CollectionsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.config.LanguageVersion; +import org.jetbrains.kotlin.descriptors.MemberDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.diagnostics.UnboundDiagnostic; import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement; import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility; +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible; import org.jetbrains.kotlin.types.KotlinTypeKt; import org.jetbrains.kotlin.util.OperatorNameConventions; import org.jetbrains.kotlin.utils.addToStdlib.AddToStdlibKt; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.List; -import java.util.ServiceLoader; +import java.util.*; import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; @@ -307,16 +308,16 @@ public class DefaultErrorMessages { "Please add the corresponding file to compilation sources"); MAP.put(NO_ACTUAL_FOR_EXPECT, "Expected {0} has no actual declaration in module {1}{2}", DECLARATION_NAME_WITH_KIND, - MODULE_WITH_PLATFORM, PlatformIncompatibilityDiagnosticRenderer.TEXT); + MODULE_WITH_PLATFORM, adaptGenerics1(PlatformIncompatibilityDiagnosticRenderer.TEXT)); MAP.put(ACTUAL_WITHOUT_EXPECT, "{0} has no corresponding expected declaration{1}", CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM, - PlatformIncompatibilityDiagnosticRenderer.TEXT); + adaptGenerics1(PlatformIncompatibilityDiagnosticRenderer.TEXT)); MAP.put(AMBIGUOUS_ACTUALS, "{0} has several compatible actual declarations in modules {1}", CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM, commaSeparated( MODULE)); MAP.put(AMBIGUOUS_EXPECTS, "{0} has several compatible expect declarations in modules {1}", CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM, commaSeparated( MODULE)); MAP.put(NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, "Actual class ''{0}'' has no corresponding members for expected class members:{1}", - NAME, IncompatibleExpectedActualClassScopesRenderer.TEXT); + NAME, adaptGenerics2(IncompatibleExpectedActualClassScopesRenderer.TEXT)); MAP.put(ACTUAL_MISSING, "Declaration must be marked with 'actual'"); MAP.put(OPTIONAL_EXPECTATION_NOT_ON_EXPECTED, "'@OptionalExpectation' can only be used on an expected annotation class"); @@ -1100,6 +1101,15 @@ public class DefaultErrorMessages { } } + // Those methods are needed to fix problems with java type system and kotlin variance + public static DiagnosticParameterRenderer, Collection>> adaptGenerics1(DiagnosticParameterRenderer, ? extends Collection>> renderer) { + return (obj, renderingContext) -> renderer.render((Map)obj, renderingContext); + } + + public static DiagnosticParameterRenderer, Collection>>>> adaptGenerics2(DiagnosticParameterRenderer, ? extends Collection>>>> renderer) { + return (obj, renderingContext) -> renderer.render((List)obj, renderingContext); + } + private DefaultErrorMessages() { } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt index fc870ef90f3..8fe76f3f720 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt @@ -18,13 +18,13 @@ package org.jetbrains.kotlin.diagnostics.rendering import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible class PlatformIncompatibilityDiagnosticRenderer( private val mode: MultiplatformDiagnosticRenderingMode -) : DiagnosticParameterRenderer>> { +) : DiagnosticParameterRenderer, Collection>> { override fun render( - obj: Map>, + obj: Map, Collection>, renderingContext: RenderingContext ): String { if (obj.isEmpty()) return "" @@ -43,9 +43,9 @@ class PlatformIncompatibilityDiagnosticRenderer( class IncompatibleExpectedActualClassScopesRenderer( private val mode: MultiplatformDiagnosticRenderingMode -) : DiagnosticParameterRenderer>>>> { +) : DiagnosticParameterRenderer, Collection>>>> { override fun render( - obj: List>>>, + obj: List, Collection>>>, renderingContext: RenderingContext ): String { if (obj.isEmpty()) return "" @@ -82,7 +82,7 @@ open class MultiplatformDiagnosticRenderingMode { } private fun StringBuilder.renderIncompatibilityInformation( - map: Map>, + map: Map, Collection>, indent: String, context: RenderingContext, mode: MultiplatformDiagnosticRenderingMode @@ -109,7 +109,7 @@ private fun StringBuilder.renderIncompatibilityInformation( } private fun StringBuilder.renderIncompatibleClassScopes( - unfulfilled: List>>>, + unfulfilled: List, Collection>>>, indent: String, context: RenderingContext, mode: MultiplatformDiagnosticRenderingMode 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 eeeea8681d4..56705454be3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt @@ -33,10 +33,9 @@ import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor import org.jetbrains.kotlin.resolve.descriptorUtil.isPrimaryConstructorOfInlineClass import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.* import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Compatible -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ModuleFilter import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil import org.jetbrains.kotlin.resolve.source.PsiSourceFile @@ -136,7 +135,7 @@ class ExpectedActualDeclarationChecker( val compatibility = path.nodes .mapNotNull { ExpectedActualResolver.findActualForExpected(descriptor, it, moduleVisibilityFilter) } .ifEmpty { return } - .fold(LinkedHashMap>()) { resultMap, partialMap -> + .fold(LinkedHashMap, List>()) { resultMap, partialMap -> resultMap.apply { putAll(partialMap) } } @@ -180,7 +179,7 @@ class ExpectedActualDeclarationChecker( ) { assert(compatibility.keys.all { it is Incompatible }) @Suppress("UNCHECKED_CAST") - val incompatibility = compatibility as Map> + val incompatibility = compatibility as Map, Collection> trace.report(Errors.NO_ACTUAL_FOR_EXPECT.on(reportOn, descriptor, module, incompatibility)) return } @@ -188,14 +187,14 @@ class ExpectedActualDeclarationChecker( // Here we have exactly one compatible actual and/or some weakly incompatible. In either case, we don't report anything on expect val actualMembers = compatibility.asSequence() .filter { (compatibility, _) -> - compatibility is Compatible || (compatibility is Incompatible && compatibility.kind != Compatibility.IncompatibilityKind.STRONG) + compatibility is Compatible || (compatibility is Incompatible && compatibility.kind != IncompatibilityKind.STRONG) }.flatMap { it.value.asSequence() } expectActualTracker.reportExpectActual(expected = descriptor, actualMembers = actualMembers) } private fun MemberDescriptor.hasNoActualWithDiagnostic( - compatibility: Map> + compatibility: Map, List> ): Boolean { return compatibility.values.flatMapTo(hashSetOf()) { it }.all { actual -> val expectedOnes = ExpectedActualResolver.findExpectedForActual(actual, module) @@ -257,7 +256,7 @@ class ExpectedActualDeclarationChecker( // This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported // later when this checker is called for them fun hasSingleActualSuspect( - expectedWithIncompatibility: Pair>> + expectedWithIncompatibility: Pair, Collection>> ): Boolean { val (expectedMember, incompatibility) = expectedWithIncompatibility val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() @@ -285,7 +284,7 @@ class ExpectedActualDeclarationChecker( } else if (Compatible !in compatibility) { assert(compatibility.keys.all { it is Incompatible }) @Suppress("UNCHECKED_CAST") - val incompatibility = compatibility as Map> + val incompatibility = compatibility as Map, Collection> trace.report(Errors.ACTUAL_WITHOUT_EXPECT.on(reportOn, descriptor, incompatibility)) } else { val expected = compatibility[Compatible]!!.first() @@ -302,7 +301,7 @@ class ExpectedActualDeclarationChecker( } private fun checkAmbiguousExpects( - compatibility: Map>, + compatibility: Map, List>, trace: BindingTrace, reportOn: KtNamedDeclaration, descriptor: MemberDescriptor @@ -322,9 +321,9 @@ class ExpectedActualDeclarationChecker( } } - private fun Compatibility.isCompatibleOrWeakCompatible() = + private fun ExpectActualCompatibility.isCompatibleOrWeakCompatible() = this is Compatible || - this is Incompatible && kind == ExpectedActualResolver.Compatibility.IncompatibilityKind.WEAK + this is Incompatible && kind == IncompatibilityKind.WEAK // we don't require `actual` modifier on // - annotation constructors, because annotation classes can only have one constructor @@ -390,7 +389,7 @@ class ExpectedActualDeclarationChecker( } companion object { - fun Map>.allStrongIncompatibilities(): Boolean = - this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG } + fun Map, Collection>.allStrongIncompatibilities(): Boolean = + this.keys.all { it is Incompatible && it.kind == IncompatibilityKind.STRONG } } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt new file mode 100644 index 00000000000..39ac11c1d92 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.multiplatform + +sealed class ExpectActualCompatibility { + // For IncompatibilityKind.STRONG `actual` declaration is considered as overload and error reports on expected declaration + enum class IncompatibilityKind { + WEAK, STRONG + } + + // Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer + sealed class Incompatible( + val reason: String?, + val kind: IncompatibilityKind = IncompatibilityKind.WEAK + ) : ExpectActualCompatibility() { + // Callables + + object CallableKind : Incompatible( + "callable kinds are different (function vs property)", + IncompatibilityKind.STRONG + ) + + object ParameterShape : Incompatible( + "parameter shapes are different (extension vs non-extension)", + IncompatibilityKind.STRONG + ) + + object ParameterCount : Incompatible("number of value parameters is different", IncompatibilityKind.STRONG) + object TypeParameterCount : Incompatible("number of type parameters is different", IncompatibilityKind.STRONG) + + object ParameterTypes : Incompatible("parameter types are different", IncompatibilityKind.STRONG) + object ReturnType : Incompatible("return type is different", IncompatibilityKind.STRONG) + + object ParameterNames : Incompatible("parameter names are different") + object TypeParameterNames : Incompatible("names of type parameters are different") + + object ValueParameterVararg : Incompatible("some value parameter is vararg in one declaration and non-vararg in the other") + object ValueParameterNoinline : Incompatible( + "some value parameter is noinline in one declaration and not noinline in the other" + ) + + object ValueParameterCrossinline : Incompatible( + "some value parameter is crossinline in one declaration and not crossinline in the other" + ) + + // Functions + + object FunctionModifiersDifferent : Incompatible("modifiers are different (suspend)") + object FunctionModifiersNotSubset : Incompatible( + "some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec)" + ) + + // Properties + + object PropertyKind : Incompatible("property kinds are different (val vs var)") + object PropertyModifiers : Incompatible("modifiers are different (const, lateinit)") + + // Classifiers + + object ClassKind : Incompatible("class kinds are different (class, interface, object, enum, annotation)") + + object ClassModifiers : Incompatible("modifiers are different (companion, inner, inline)") + + object Supertypes : Incompatible("some supertypes are missing in the actual declaration") + + class ClassScopes( + val unfulfilled: List, Collection>>> + ) : Incompatible("some expected members have no actual ones") + + object EnumEntries : Incompatible("some entries from expected enum are missing in the actual enum") + + // Common + + object Modality : Incompatible("modality is different") + object Visibility : Incompatible("visibility is different") + + object TypeParameterUpperBounds : Incompatible("upper bounds of type parameters are different", IncompatibilityKind.STRONG) + object TypeParameterVariance : Incompatible("declaration-site variances of type parameters are different") + object TypeParameterReified : Incompatible( + "some type parameter is reified in one declaration and non-reified in the other" + ) + + object Unknown : Incompatible(null) + } + + object Compatible : ExpectActualCompatibility() +} 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 26fc7a859c3..c3ed18542e5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt @@ -13,8 +13,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Compatible -import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility.Incompatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Compatible +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -49,7 +49,7 @@ object ExpectedActualResolver { expected: MemberDescriptor, platformModule: ModuleDescriptor, moduleVisibilityFilter: ModuleFilter = onlyFromThisModule(platformModule) - ): Map>? { + ): Map, List>? { return when (expected) { is CallableMemberDescriptor -> { expected.findNamesakesFromModule(platformModule, moduleVisibilityFilter).filter { actual -> @@ -78,7 +78,7 @@ object ExpectedActualResolver { actual: MemberDescriptor, commonModule: ModuleDescriptor, moduleFilter: (ModuleDescriptor) -> Boolean = onlyFromThisModule(commonModule) - ): Map>? { + ): Map, List>? { return when (actual) { is CallableMemberDescriptor -> { val container = actual.containingDeclaration @@ -180,79 +180,13 @@ object ExpectedActualResolver { return classifiers } - sealed class Compatibility { - // For IncompatibilityKind.STRONG `actual` declaration is considered as overload and error reports on expected declaration - enum class IncompatibilityKind { - WEAK, STRONG - } - - // Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer - sealed class Incompatible(val reason: String?, val kind: IncompatibilityKind = IncompatibilityKind.WEAK) : Compatibility() { - // Callables - - object CallableKind : Incompatible("callable kinds are different (function vs property)", IncompatibilityKind.STRONG) - - object ParameterShape : Incompatible("parameter shapes are different (extension vs non-extension)", IncompatibilityKind.STRONG) - - object ParameterCount : Incompatible("number of value parameters is different", IncompatibilityKind.STRONG) - object TypeParameterCount : Incompatible("number of type parameters is different", IncompatibilityKind.STRONG) - - object ParameterTypes : Incompatible("parameter types are different", IncompatibilityKind.STRONG) - object ReturnType : Incompatible("return type is different", IncompatibilityKind.STRONG) - - object ParameterNames : Incompatible("parameter names are different") - object TypeParameterNames : Incompatible("names of type parameters are different") - - object ValueParameterVararg : Incompatible("some value parameter is vararg in one declaration and non-vararg in the other") - object ValueParameterNoinline : Incompatible("some value parameter is noinline in one declaration and not noinline in the other") - object ValueParameterCrossinline : Incompatible("some value parameter is crossinline in one declaration and not crossinline in the other") - - // Functions - - object FunctionModifiersDifferent : Incompatible("modifiers are different (suspend)") - object FunctionModifiersNotSubset : Incompatible("some modifiers on expected declaration are missing on the actual one (external, infix, inline, operator, tailrec)") - - // Properties - - object PropertyKind : Incompatible("property kinds are different (val vs var)") - object PropertyModifiers : Incompatible("modifiers are different (const, lateinit)") - - // Classifiers - - object ClassKind : Incompatible("class kinds are different (class, interface, object, enum, annotation)") - - object ClassModifiers : Incompatible("modifiers are different (companion, inner, inline)") - - object Supertypes : Incompatible("some supertypes are missing in the actual declaration") - - class ClassScopes( - val unfulfilled: List>>> - ) : Incompatible("some expected members have no actual ones") - - object EnumEntries : Incompatible("some entries from expected enum are missing in the actual enum") - - // Common - - object Modality : Incompatible("modality is different") - object Visibility : Incompatible("visibility is different") - - object TypeParameterUpperBounds : Incompatible("upper bounds of type parameters are different", IncompatibilityKind.STRONG) - object TypeParameterVariance : Incompatible("declaration-site variances of type parameters are different") - object TypeParameterReified : Incompatible("some type parameter is reified in one declaration and non-reified in the other") - - object Unknown : Incompatible(null) - } - - object Compatible : Compatibility() - } - // a is the declaration in common code, b is the definition in the platform-specific code private fun areCompatibleCallables( a: CallableMemberDescriptor, b: CallableMemberDescriptor, platformModule: ModuleDescriptor = b.module, parentSubstitutor: Substitutor? = null - ): Compatibility { + ): ExpectActualCompatibility { assert(a.name == b.name) { "This function should be invoked only for declarations with the same name: $a, $b" } @@ -376,7 +310,7 @@ object ExpectedActualResolver { b: List, platformModule: ModuleDescriptor, substitutor: Substitutor - ): Compatibility { + ): ExpectActualCompatibility { for (i in a.indices) { val aBounds = a[i].upperBounds val bBounds = b[i].upperBounds @@ -393,7 +327,7 @@ object ExpectedActualResolver { return Compatible } - private fun areCompatibleFunctions(a: FunctionDescriptor, b: FunctionDescriptor): Compatibility { + private fun areCompatibleFunctions(a: FunctionDescriptor, b: FunctionDescriptor): ExpectActualCompatibility { if (!equalBy(a, b) { f -> f.isSuspend }) return Incompatible.FunctionModifiersDifferent if (a.isExternal && !b.isExternal || @@ -405,14 +339,14 @@ object ExpectedActualResolver { return Compatible } - private fun areCompatibleProperties(a: PropertyDescriptor, b: PropertyDescriptor): Compatibility { + private fun areCompatibleProperties(a: PropertyDescriptor, b: PropertyDescriptor): ExpectActualCompatibility { if (!equalBy(a, b) { p -> p.isVar }) return Incompatible.PropertyKind if (!equalBy(a, b) { p -> listOf(p.isConst, p.isLateInit) }) return Incompatible.PropertyModifiers return Compatible } - private fun areCompatibleClassifiers(a: ClassDescriptor, other: ClassifierDescriptor): Compatibility { + private fun areCompatibleClassifiers(a: ClassDescriptor, other: ClassifierDescriptor): ExpectActualCompatibility { // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name assert(a.name == other.name) { "This function should be invoked only for declarations with the same name: $a, $other" } @@ -476,8 +410,8 @@ object ExpectedActualResolver { b: ClassDescriptor, platformModule: ModuleDescriptor, substitutor: Substitutor - ): Compatibility { - val unfulfilled = arrayListOf>>>() + ): ExpectActualCompatibility { + val unfulfilled = arrayListOf, MutableCollection>>>() val bMembersByName = b.getMembers().groupBy { it.name } @@ -500,7 +434,7 @@ object ExpectedActualResolver { } if (mapping.values.any { it == Compatible }) continue - val incompatibilityMap = mutableMapOf>() + val incompatibilityMap = mutableMapOf, MutableCollection>() for ((descriptor, compatibility) in mapping) { when (compatibility) { Compatible -> continue@outer diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt index 6d30341897f..e8a74a80a64 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/expectActualUtil.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver internal fun MemberDescriptor.expectedDescriptors() = @@ -74,7 +75,7 @@ fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declaratio private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor): DeclarationDescriptor? = with(ExpectedActualResolver) { val expectedCompatibilityMap = findExpectedForActual(descriptor, this@declarationOf) - expectedCompatibilityMap?.get(ExpectedActualResolver.Compatibility.Compatible)?.firstOrNull() + expectedCompatibilityMap?.get(ExpectActualCompatibility.Compatible)?.firstOrNull() ?: expectedCompatibilityMap?.values?.flatten()?.firstOrNull() } @@ -181,4 +182,4 @@ fun KtDeclaration.runCommandOnAllExpectAndActualDeclaration( } else { project.executeCommand(command, command = ::process) } -} \ No newline at end of file +} diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/OptionalExpectationInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/OptionalExpectationInspection.kt index f828d59f9a3..81815e58543 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/OptionalExpectationInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/OptionalExpectationInspection.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker.Co import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility class OptionalExpectationInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = @@ -43,12 +44,12 @@ class OptionalExpectationInspection : AbstractKotlinInspection() { for (actualModuleDescriptor in implementingModules) { val compatibility = ExpectedActualResolver.findActualForExpected(descriptor, actualModuleDescriptor) ?: continue if (!compatibility.allStrongIncompatibilities() && - (ExpectedActualResolver.Compatibility.Compatible in compatibility || + (ExpectActualCompatibility.Compatible in compatibility || !compatibility.values.flatMapTo( hashSetOf() ) { it }.all { actual -> val expectedOnes = ExpectedActualResolver.findExpectedForActual(actual, descriptor.module) - expectedOnes != null && ExpectedActualResolver.Compatibility.Compatible in expectedOnes.keys + expectedOnes != null && ExpectActualCompatibility.Compatible in expectedOnes.keys }) ) continue val platform = actualModuleDescriptor.platform ?: continue @@ -69,4 +70,4 @@ class OptionalExpectationInspection : AbstractKotlinInspection() { ) } }) -} \ No newline at end of file +}