Don't report incompatibility expect/actual errors on usual overloads

Divide incompatibility on two groups: strong and weak. Strong incompatibility means that if declaration with such incompatibility has no `actual` modifier then it's considered as usual overload and we'll not report any error on it.

 #KT-20540 Fixed
 #KT-20680 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2017-10-11 19:28:53 +03:00
parent eb828e3497
commit d0cbd73307
13 changed files with 258 additions and 10 deletions
@@ -84,7 +84,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val compatibility = findActualForExpected(descriptor, platformModule) ?: return
val shouldReportError =
compatibility.isEmpty() ||
compatibility.allStrongIncompatibilities() ||
Compatible !in compatibility && compatibility.values.flatMapTo(hashSetOf()) { it }.all { actual ->
val expectedOnes = findExpectedForActual(actual, descriptor.module)
expectedOnes != null && Compatible in expectedOnes.keys
@@ -98,6 +98,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
}
}
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
private fun findActualForExpected(expected: MemberDescriptor, platformModule: ModuleDescriptor): Map<Compatibility, List<MemberDescriptor>>? {
return when (expected) {
is CallableMemberDescriptor -> {
@@ -131,7 +134,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val hasActualModifier = descriptor.isActual && reportOn.hasActualModifier()
if (!hasActualModifier) {
if (compatibility.isEmpty()) return
if (compatibility.allStrongIncompatibilities()) return
if (Compatible in compatibility) {
// we suppress error, because annotation classes can only have one constructor and it's a 100% boilerplate
@@ -163,6 +166,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
return actualMember != null &&
actualMember.isExplicitActualDeclaration() &&
!incompatibility.allStrongIncompatibilities() &&
findExpectedForActual(actualMember, expectedMember.module)?.values?.singleOrNull()?.singleOrNull() == expectedMember
}
@@ -293,16 +297,21 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
}
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?) : Compatibility() {
sealed class Incompatible(val reason: String?, val kind: IncompatibilityKind = IncompatibilityKind.WEAK) : Compatibility() {
// Callables
object ParameterShape : Incompatible("parameter shapes are different (extension vs non-extension)")
object ParameterCount : Incompatible("number of value parameters is different")
object ParameterCount : Incompatible("number of value parameters is different", IncompatibilityKind.STRONG)
object TypeParameterCount : Incompatible("number of type parameters is different")
object ParameterTypes : Incompatible("parameter types are different")
object ParameterTypes : Incompatible("parameter types are different", IncompatibilityKind.STRONG)
object ReturnType : Incompatible("return type is different")
object ParameterNames : Incompatible("parameter names are different")