[MPP] Fix checkExpectedParameter using wrong ModuleFilter

KT-50120 Partially solved for functions.
ClassConstructors seem to be deserialized with 'isExpect=false'
which will still lead to wrong 'expect/actual matching'
This commit is contained in:
sebastian.sellmair
2021-12-15 16:19:47 +01:00
committed by Space
parent 017aacba44
commit e561b095f7
5 changed files with 25 additions and 32 deletions
@@ -17,10 +17,11 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
import org.jetbrains.kotlin.resolve.multiplatform.onlyFromThisModule
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.getExceptionMessage
import org.jetbrains.kotlin.util.getNonPrivateTraitMembersForDelegation
@@ -182,7 +183,7 @@ object CodegenUtil {
@JvmStatic
fun findExpectedFunctionForActual(descriptor: FunctionDescriptor): FunctionDescriptor? {
val compatibleExpectedFunctions = with(ExpectedActualResolver) {
descriptor.findCompatibleExpectedForActual(DescriptorUtils.getContainingModule(descriptor))
descriptor.findCompatibleExpectedForActual(onlyFromThisModule(descriptor.module))
}
return compatibleExpectedFunctions.firstOrNull() as FunctionDescriptor?
}
@@ -30,11 +30,8 @@ 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.*
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.*
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
import org.jetbrains.kotlin.resolve.multiplatform.ModuleFilter
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.types.KotlinType
@@ -69,13 +66,13 @@ class ExpectedActualDeclarationChecker(
checkActualModifier, context.expectActualTracker
)
} else if (descriptor.isActualOrSomeContainerIsActual()) {
val allImplementedModules = moduleStructureOracle.findAllDependsOnPaths(descriptor.module).flatMap { it.nodes }.toHashSet()
val allDependsOnModules = moduleStructureOracle.findAllDependsOnPaths(descriptor.module).flatMap { it.nodes }.toHashSet()
checkActualDeclarationHasExpected(
declaration,
descriptor,
checkActualModifier,
context.trace,
moduleVisibilityFilter = { it in allImplementedModules }
moduleVisibilityFilter = { it in allDependsOnModules }
)
}
}
@@ -222,7 +219,7 @@ class ExpectedActualDeclarationChecker(
compatibility: Map<ExpectActualCompatibility<MemberDescriptor>, List<MemberDescriptor>>
): Boolean {
return compatibility.values.flatMapTo(hashSetOf()) { it }.all { actual ->
val expectedOnes = ExpectedActualResolver.findExpectedForActual(actual, module)
val expectedOnes = ExpectedActualResolver.findExpectedForActual(actual, onlyFromThisModule(module))
expectedOnes != null && Compatible in expectedOnes.keys
}
}
@@ -250,7 +247,7 @@ class ExpectedActualDeclarationChecker(
trace: BindingTrace,
moduleVisibilityFilter: ModuleFilter
) {
val compatibility = ExpectedActualResolver.findExpectedForActual(descriptor, descriptor.module, moduleVisibilityFilter)
val compatibility = ExpectedActualResolver.findExpectedForActual(descriptor, moduleVisibilityFilter)
?: return
checkAmbiguousExpects(compatibility, trace, reportOn, descriptor)
@@ -290,8 +287,7 @@ class ExpectedActualDeclarationChecker(
actualMember.isExplicitActualDeclaration() &&
!incompatibility.allStrongIncompatibilities() &&
ExpectedActualResolver.findExpectedForActual(
actualMember,
expectedMember.module
actualMember, onlyFromThisModule(expectedMember.module)
)?.values?.singleOrNull()?.singleOrNull() == expectedMember
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
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.onlyFromThisModule
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
// `doRemove` means should expect-declaration be removed from IR
@@ -143,7 +144,7 @@ class ExpectDeclarationRemover(val symbolTable: ReferenceSymbolTable, private va
private fun MemberDescriptor.findExpectForActual(): MemberDescriptor? {
if (!isActual) error(this)
return with(ExpectedActualResolver) {
findCompatibleExpectedForActual(this@findExpectForActual.module).singleOrNull()
findCompatibleExpectedForActual(onlyFromThisModule(this@findExpectForActual.module)).singleOrNull()
}
}
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.resolve.calls.model.CollectionLiteralKotlinCallArgum
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.isParameterOfAnnotation
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.multiplatform.ALL_MODULES
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.UnwrappedType
@@ -113,7 +113,7 @@ private fun ValueParameterDescriptor.checkExpectedParameter(checker: (ValueParam
val function = containingDeclaration
if (function is FunctionDescriptor && function.isActual) {
with(ExpectedActualResolver) {
val expected = function.findCompatibleExpectedForActual(function.module).firstOrNull()
val expected = function.findCompatibleExpectedForActual(ALL_MODULES).firstOrNull()
return expected is FunctionDescriptor && checker(expected.valueParameters[index])
}
}
@@ -26,22 +26,22 @@ import org.jetbrains.kotlin.utils.keysToMap
object ExpectedActualResolver {
// FIXME(dsavvinov): review clients, as they won't work properly in HMPP projects
fun MemberDescriptor.findCompatibleActualForExpected(platformModule: ModuleDescriptor): List<MemberDescriptor> =
findActualForExpected(this, platformModule)?.get(Compatible).orEmpty()
findActualForExpected(this, platformModule, onlyFromThisModule(platformModule))?.get(Compatible).orEmpty()
fun MemberDescriptor.findAnyActualForExpected(platformModule: ModuleDescriptor): List<MemberDescriptor> {
val actualsGroupedByCompatibility = findActualForExpected(this, platformModule)
val actualsGroupedByCompatibility = findActualForExpected(this, platformModule, onlyFromThisModule(platformModule))
return actualsGroupedByCompatibility?.get(Compatible)
?: actualsGroupedByCompatibility?.values?.flatten()
?: emptyList()
}
fun MemberDescriptor.findCompatibleExpectedForActual(commonModule: ModuleDescriptor): List<MemberDescriptor> =
findExpectedForActual(this, commonModule)?.get(Compatible).orEmpty()
fun MemberDescriptor.findCompatibleExpectedForActual(moduleFilter: ModuleFilter): List<MemberDescriptor> =
findExpectedForActual(this, moduleFilter)?.get(Compatible).orEmpty()
fun findActualForExpected(
expected: MemberDescriptor,
platformModule: ModuleDescriptor,
moduleVisibilityFilter: ModuleFilter = onlyFromThisModule(platformModule)
moduleVisibilityFilter: ModuleFilter,
): Map<ExpectActualCompatibility<MemberDescriptor>, List<MemberDescriptor>>? {
return when (expected) {
is CallableMemberDescriptor -> {
@@ -69,8 +69,7 @@ object ExpectedActualResolver {
fun findExpectedForActual(
actual: MemberDescriptor,
commonModule: ModuleDescriptor,
moduleFilter: (ModuleDescriptor) -> Boolean = onlyFromThisModule(commonModule)
moduleFilter: (ModuleDescriptor) -> Boolean
): Map<ExpectActualCompatibility<MemberDescriptor>, List<MemberDescriptor>>? {
return when (actual) {
is CallableMemberDescriptor -> {
@@ -79,10 +78,10 @@ object ExpectedActualResolver {
is ClassifierDescriptorWithTypeParameters -> {
// TODO: replace with 'singleOrNull' as soon as multi-module diagnostic tests are refactored
val expectedClass =
findExpectedForActual(container, commonModule, moduleFilter)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor
findExpectedForActual(container, moduleFilter)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor
expectedClass?.getMembers(actual.name)?.filterIsInstance<CallableMemberDescriptor>().orEmpty()
}
is PackageFragmentDescriptor -> actual.findNamesakesFromModule(commonModule, moduleFilter)
is PackageFragmentDescriptor -> actual.findNamesakesFromModule(actual.module, moduleFilter)
else -> return null // do not report anything for incorrect code, e.g. 'actual' local function
}
@@ -101,7 +100,7 @@ object ExpectedActualResolver {
}
}
is ClassifierDescriptorWithTypeParameters -> {
actual.findClassifiersFromModule(commonModule, moduleFilter).filter { declaration ->
actual.findClassifiersFromModule(actual.module, moduleFilter).filter { declaration ->
actual != declaration &&
declaration is ClassDescriptor && declaration.isExpect
}.groupBy { expected ->
@@ -534,19 +533,15 @@ object ExpectedActualResolver {
}
}
fun DeclarationDescriptor.findExpects(inModule: ModuleDescriptor = this.module): List<MemberDescriptor> {
fun DeclarationDescriptor.findExpects(): List<MemberDescriptor> {
return ExpectedActualResolver.findExpectedForActual(
this as MemberDescriptor,
inModule,
{ true }
this as MemberDescriptor, ALL_MODULES
)?.get(Compatible).orEmpty()
}
fun DeclarationDescriptor.findActuals(inModule: ModuleDescriptor = this.module): List<MemberDescriptor> {
return ExpectedActualResolver.findActualForExpected(
(this as MemberDescriptor),
inModule,
{ true }
(this as MemberDescriptor), inModule, ALL_MODULES
)?.get(Compatible).orEmpty()
}