[kotlin] k2: inspection to detect ambiguous actuals

- k2 moved ambiguous actuals detection from frontend to ir, and thus compiler error is not available anymore;

^KTIJ-33753 fixed

GitOrigin-RevId: c17ad30a7f7544087ffdaa500644a2686193f120
This commit is contained in:
Anna Kozlova
2025-06-05 16:23:31 +00:00
committed by intellij-monorepo-bot
parent d1061e591d
commit fb613ad51e
59 changed files with 585 additions and 320 deletions
@@ -2837,3 +2837,5 @@ java.collection.is.parameterized.with.nullable.type=Java collection ''{0}'' is p
inspection.nullable.type.argument.in.java.collection.display.name=Nullable type argument in Java collection that doesn't support nulls
replace.nullable.type.with.non.nullable.family.name=Replace nullable type with non-nullable
java.collections.to.process=Java collections to process:
inspection.ambiguous.actuals.display.name=Ambiguous actuals
ambiguous.actuals.for.0={0} has several compatible actual declarations in modules {1}
@@ -0,0 +1,17 @@
<html>
<body>
Reports <code>expect</code> declarations that have ambiguous
<code>actual</code> declarations in implementing multi-platform modules.
<p>
For example:
</p>
<pre><code>
// common.kt
expect class Foo
// middle.kt
actual class Foo
// bottom.kt
actual class Foo
</code></pre>
</body>
</html>
@@ -397,6 +397,13 @@
level="ERROR"
language="kotlin" key="inspection.no.actual.for.expect.display.name" bundle="messages.KotlinBundle"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinAmbiguousActualsInspection"
groupPath="Kotlin"
groupBundle="messages.KotlinBundle" groupKey="group.names.other.problems"
enabledByDefault="true"
level="ERROR"
language="kotlin" key="inspection.ambiguous.actuals.display.name" bundle="messages.KotlinBundle"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.PackageDirectoryMismatchInspection"
groupPath="Kotlin"
groupBundle="messages.KotlinBundle" groupKey="group.names.java.interop.issues"
@@ -0,0 +1,46 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.codeinsight.inspections
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.base.facet.implementedModules
import org.jetbrains.kotlin.idea.base.facet.isMultiPlatformModule
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.base.util.module
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.searching.kmp.findAllActualForExpect
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.isExpectDeclaration
class KotlinAmbiguousActualsInspection : AbstractKotlinInspection() {
override fun buildVisitor(
holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession
): PsiElementVisitor {
val module = holder.file.module ?: return PsiElementVisitor.EMPTY_VISITOR
if (!module.isMultiPlatformModule) return PsiElementVisitor.EMPTY_VISITOR
return object : KtVisitorVoid() {
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
val nameIdentifier = declaration.nameIdentifier ?: return
if (!declaration.isExpectDeclaration()) return
val foundActuals = declaration.findAllActualForExpect(compatibleOnly = false).mapNotNull { it.element?.module }.toSet()
val ambiguous = foundActuals.flatMap { act ->
val declaredModules = act.implementedModules.filter { it in foundActuals }
if (declaredModules.isEmpty()) emptyList() else buildList {
add(act)
addAll(declaredModules)
}
}
if (ambiguous.isNotEmpty()) {
holder.registerProblem(nameIdentifier, KotlinBundle.message("ambiguous.actuals.for.0", declaration.name.toString(), ambiguous.joinToString(", ") { it.name }))
}
}
}
}
}
@@ -0,0 +1,66 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.inspections.tests
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.base.test.IgnoreTests
import org.jetbrains.kotlin.idea.base.test.InTextDirectivesUtils
import org.jetbrains.kotlin.idea.inspections.runInspection
import org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinAmbiguousActualsInspection
import org.jetbrains.kotlin.idea.multiplatform.setupMppProjectFromDirStructure
import org.jetbrains.kotlin.idea.test.*
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
abstract class AbstractK2AmbiguousActualsTest : AbstractMultiModuleTest() {
protected fun dataFile(fileName: String): File = File(testDataPath, fileName)
protected fun dataFile(): File = dataFile(fileName())
protected open fun fileName(): String = KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt")
override fun getTestDataDirectory(): File {
return File(TestMetadataUtil.getTestDataPath(this::class.java))
}
fun doTest(unused: String) {
setupMppProjectFromDirStructure(dataFile())
val actionFile = project.allKotlinFiles().single {
it.name == "top.kt" || it.name == "common-1.kt"
}
val virtualFilePath = actionFile.virtualFile!!.toNioPath()
val ignoreDirective = IgnoreTests.DIRECTIVES.of(pluginMode)
IgnoreTests.runTestIfNotDisabledByFileDirective(virtualFilePath, ignoreDirective) {
val directiveFileText = actionFile.text
withCustomCompilerOptions(directiveFileText, project, module) {
val fileText = actionFile.text
val ambiguousActualDirective = InTextDirectivesUtils.findLineWithPrefixRemoved(fileText, "// AMBIGUOUS_ACTUALS:")
assertNotNull("Missing AMBIGUOUS_ACTUALS declaration in config file", ambiguousActualDirective)
val expectedAmbiguousActualModules = ambiguousActualDirective!!.substringAfter(":").trim()
.takeIf { !it.isEmpty() }?.split(",")?.map { it.trim() } ?: emptyList()
val problemDescriptors = runInspection(
KotlinAmbiguousActualsInspection::class.java,
project,
settings = null
).problemElements.values.map { it.descriptionTemplate }
val ambiguousActuals = if (problemDescriptors.isEmpty()) {
emptyList()
} else {
val differentErrors =
problemDescriptors.map { it.substringAfter("in modules", "").trim().split(",").map { it.trim() }.filter { !it.isBlank() } }
.distinct()
assertTrue("Found more than one problemDescriptor", differentErrors.size <= 1)
differentErrors.firstOrNull() ?: emptyList()
}
assertSameElements(ambiguousActuals, expectedAmbiguousActualModules)
}
}
}
protected open fun findAfterFile(editedFile: KtFile): PsiFile? = editedFile.containingDirectory?.findFile(editedFile.name + ".after")
}
@@ -0,0 +1,82 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.inspections.tests;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
import org.jetbrains.kotlin.idea.base.test.TestRoot;
import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
/**
* This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
* DO NOT MODIFY MANUALLY.
*/
@SuppressWarnings("all")
@TestRoot("code-insight/inspections-k2/tests")
@TestDataPath("$CONTENT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/multiplatform/ambiguousActuals")
public class K2AmbiguousActualsTestGenerated extends AbstractK2AmbiguousActualsTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("diamondActualInBottom")
public void testDiamondActualInBottom() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondActualInBottom/");
}
@TestMetadata("diamondActualOnOnePath")
public void testDiamondActualOnOnePath() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondActualOnOnePath/");
}
@TestMetadata("diamondDuplicateActuals")
public void testDiamondDuplicateActuals() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondDuplicateActuals/");
}
@TestMetadata("diamondModuleDependency1")
public void testDiamondModuleDependency1() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondModuleDependency1/");
}
@TestMetadata("diamondModuleDependency2")
public void testDiamondModuleDependency2() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondModuleDependency2/");
}
@TestMetadata("diamondSeesTwoActuals")
public void testDiamondSeesTwoActuals() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/diamondSeesTwoActuals/");
}
@TestMetadata("duplicateActualsExplicit")
public void testDuplicateActualsExplicit() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/duplicateActualsExplicit/");
}
@TestMetadata("duplicateActualsImplicit")
public void testDuplicateActualsImplicit() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/duplicateActualsImplicit/");
}
@TestMetadata("duplicateActualsOneWeaklyIncompatible")
public void testDuplicateActualsOneWeaklyIncompatible() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/duplicateActualsOneWeaklyIncompatible/");
}
@TestMetadata("duplicateActualsOneWithStrongIncompatibility")
public void testDuplicateActualsOneWithStrongIncompatibility() throws Exception {
runTest("../../../idea/tests/testData/multiplatform/ambiguousActuals/duplicateActualsOneWithStrongIncompatibility/");
}
}
@@ -18,325 +18,342 @@ import org.junit.runner.RunWith;
@TestRoot("idea/tests")
@TestDataPath("$CONTENT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/multiplatform")
public class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAnalysisTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K1;
public abstract class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAnalysisTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/multiplatform")
public static class Multiplatform extends AbstractMultiplatformAnalysisTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K1;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("aliasesTypeMismatch")
public void testAliasesTypeMismatch() throws Exception {
runTest("testData/multiplatform/aliasesTypeMismatch/");
}
@TestMetadata("builtinsAndStdlib")
public void testBuiltinsAndStdlib() throws Exception {
runTest("testData/multiplatform/builtinsAndStdlib/");
}
@TestMetadata("callableReferences")
public void testCallableReferences() throws Exception {
runTest("testData/multiplatform/callableReferences/");
}
@TestMetadata("chainedTypeAliasRefinement")
public void testChainedTypeAliasRefinement() throws Exception {
runTest("testData/multiplatform/chainedTypeAliasRefinement/");
}
@TestMetadata("commonSealedWithPlatformInheritor")
public void testCommonSealedWithPlatformInheritor() throws Exception {
runTest("testData/multiplatform/commonSealedWithPlatformInheritor/");
}
@TestMetadata("constructorsOfExpect")
public void testConstructorsOfExpect() throws Exception {
runTest("testData/multiplatform/constructorsOfExpect/");
}
@TestMetadata("correctOverloadResolutionAmbiguity")
public void testCorrectOverloadResolutionAmbiguity() throws Exception {
runTest("testData/multiplatform/correctOverloadResolutionAmbiguity/");
}
@TestMetadata("defaultArguments")
public void testDefaultArguments() throws Exception {
runTest("testData/multiplatform/defaultArguments/");
}
@TestMetadata("differentKindsOfDependencies")
public void testDifferentKindsOfDependencies() throws Exception {
runTest("testData/multiplatform/differentKindsOfDependencies/");
}
@TestMetadata("duplicateExpectsExplicit")
public void testDuplicateExpectsExplicit() throws Exception {
runTest("testData/multiplatform/duplicateExpectsExplicit/");
}
@TestMetadata("duplicateExpectsImplicit")
public void testDuplicateExpectsImplicit() throws Exception {
runTest("testData/multiplatform/duplicateExpectsImplicit/");
}
@TestMetadata("duplicateExpectsWithStrongIncompatibility")
public void testDuplicateExpectsWithStrongIncompatibility() throws Exception {
runTest("testData/multiplatform/duplicateExpectsWithStrongIncompatibility/");
}
@TestMetadata("enumFromCommonSerlializableSupertype")
public void testEnumFromCommonSerlializableSupertype() throws Exception {
runTest("testData/multiplatform/enumFromCommonSerlializableSupertype/");
}
@TestMetadata("expectActualLineMarkers")
public void testExpectActualLineMarkers() throws Exception {
runTest("testData/multiplatform/expectActualLineMarkers/");
}
@TestMetadata("extensionOnExpect")
public void testExtensionOnExpect() throws Exception {
runTest("testData/multiplatform/extensionOnExpect/");
}
@TestMetadata("hierarcicalActualization")
public void testHierarcicalActualization() throws Exception {
runTest("testData/multiplatform/hierarcicalActualization/");
}
@TestMetadata("incompleteActualization")
public void testIncompleteActualization() throws Exception {
runTest("testData/multiplatform/incompleteActualization/");
}
@TestMetadata("internalFromDependsOn")
public void testInternalFromDependsOn() throws Exception {
runTest("testData/multiplatform/internalFromDependsOn/");
}
@TestMetadata("internalFromDependsOnOfProduction")
public void testInternalFromDependsOnOfProduction() throws Exception {
runTest("testData/multiplatform/internalFromDependsOnOfProduction/");
}
@TestMetadata("internalFromProduction")
public void testInternalFromProduction() throws Exception {
runTest("testData/multiplatform/internalFromProduction/");
}
@TestMetadata("jsNameClash")
public void testJsNameClash() throws Exception {
runTest("testData/multiplatform/jsNameClash/");
}
@TestMetadata("jvmInlineValueClass")
public void testJvmInlineValueClass() throws Exception {
runTest("testData/multiplatform/jvmInlineValueClass/");
}
@TestMetadata("kt41218")
public void testKt41218() throws Exception {
runTest("testData/multiplatform/kt41218/");
}
@TestMetadata("kt44898")
public void testKt44898() throws Exception {
runTest("testData/multiplatform/kt44898/");
}
@TestMetadata("kt48291")
public void testKt48291() throws Exception {
runTest("testData/multiplatform/kt48291/");
}
@TestMetadata("ktij22295")
public void testKtij22295() throws Exception {
runTest("testData/multiplatform/ktij22295/");
}
@TestMetadata("ktij27523")
public void testKtij27523() throws Exception {
runTest("testData/multiplatform/ktij27523/");
}
@TestMetadata("lambdas")
public void testLambdas() throws Exception {
runTest("testData/multiplatform/lambdas/");
}
@TestMetadata("languageConstructions")
public void testLanguageConstructions() throws Exception {
runTest("testData/multiplatform/languageConstructions/");
}
@TestMetadata("multilevelParents")
public void testMultilevelParents() throws Exception {
runTest("testData/multiplatform/multilevelParents/");
}
@TestMetadata("multiplatformLibrary")
public void testMultiplatformLibrary() throws Exception {
runTest("testData/multiplatform/multiplatformLibrary/");
}
@TestMetadata("nativeStdlib")
public void testNativeStdlib() throws Exception {
runTest("testData/multiplatform/nativeStdlib/");
}
@TestMetadata("nativeTestLineMarker")
public void testNativeTestLineMarker() throws Exception {
runTest("testData/multiplatform/nativeTestLineMarker/");
}
@TestMetadata("overrideExpect")
public void testOverrideExpect() throws Exception {
runTest("testData/multiplatform/overrideExpect/");
}
@TestMetadata("overrideExpectWithCompositeType")
public void testOverrideExpectWithCompositeType() throws Exception {
runTest("testData/multiplatform/overrideExpectWithCompositeType/");
}
@TestMetadata("platformDependencyInCommon")
public void testPlatformDependencyInCommon() throws Exception {
runTest("testData/multiplatform/platformDependencyInCommon/");
}
@TestMetadata("platformSpecificChecksInCommon")
public void testPlatformSpecificChecksInCommon() throws Exception {
runTest("testData/multiplatform/platformSpecificChecksInCommon/");
}
@TestMetadata("qualifiedReceiver")
public void testQualifiedReceiver() throws Exception {
runTest("testData/multiplatform/qualifiedReceiver/");
}
@TestMetadata("recursiveTypes")
public void testRecursiveTypes() throws Exception {
runTest("testData/multiplatform/recursiveTypes/");
}
@TestMetadata("sealedInheritorsInComplexModuleStructure1")
public void testSealedInheritorsInComplexModuleStructure1() throws Exception {
runTest("testData/multiplatform/sealedInheritorsInComplexModuleStructure1/");
}
@TestMetadata("sealedInheritorsInComplexModuleStructure2")
public void testSealedInheritorsInComplexModuleStructure2() throws Exception {
runTest("testData/multiplatform/sealedInheritorsInComplexModuleStructure2/");
}
@TestMetadata("simple")
public void testSimple() throws Exception {
runTest("testData/multiplatform/simple/");
}
@TestMetadata("smartCastOnPropertyFromDependentModule")
public void testSmartCastOnPropertyFromDependentModule() throws Exception {
runTest("testData/multiplatform/smartCastOnPropertyFromDependentModule/");
}
@TestMetadata("supertypes")
public void testSupertypes() throws Exception {
runTest("testData/multiplatform/supertypes/");
}
@TestMetadata("transitiveDependencyOnCommonSourceSets")
public void testTransitiveDependencyOnCommonSourceSets() throws Exception {
runTest("testData/multiplatform/transitiveDependencyOnCommonSourceSets/");
}
@TestMetadata("typeAliasToExpectClassExplicitReference")
public void testTypeAliasToExpectClassExplicitReference() throws Exception {
runTest("testData/multiplatform/typeAliasToExpectClassExplicitReference/");
}
@TestMetadata("typeAliases")
public void testTypeAliases() throws Exception {
runTest("testData/multiplatform/typeAliases/");
}
@TestMetadata("typeParameters")
public void testTypeParameters() throws Exception {
runTest("testData/multiplatform/typeParameters/");
}
@TestMetadata("unresolvedInMultiplatformLibrary")
public void testUnresolvedInMultiplatformLibrary() throws Exception {
runTest("testData/multiplatform/unresolvedInMultiplatformLibrary/");
}
@TestMetadata("useCorrectBuiltIns")
public void testUseCorrectBuiltIns() throws Exception {
runTest("testData/multiplatform/useCorrectBuiltIns/");
}
@TestMetadata("weaklyIncompatibleActualInIntermediateModule")
public void testWeaklyIncompatibleActualInIntermediateModule() throws Exception {
runTest("testData/multiplatform/weaklyIncompatibleActualInIntermediateModule/");
}
@TestMetadata("whenExhaustivenessForSealed")
public void testWhenExhaustivenessForSealed() throws Exception {
runTest("testData/multiplatform/whenExhaustivenessForSealed/");
}
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/multiplatform/ambiguousActuals")
public static class AmbiguousActuals extends AbstractMultiplatformAnalysisTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K1;
}
@TestMetadata("aliasesTypeMismatch")
public void testAliasesTypeMismatch() throws Exception {
runTest("testData/multiplatform/aliasesTypeMismatch/");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("builtinsAndStdlib")
public void testBuiltinsAndStdlib() throws Exception {
runTest("testData/multiplatform/builtinsAndStdlib/");
}
@TestMetadata("diamondActualInBottom")
public void testDiamondActualInBottom() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondActualInBottom/");
}
@TestMetadata("callableReferences")
public void testCallableReferences() throws Exception {
runTest("testData/multiplatform/callableReferences/");
}
@TestMetadata("diamondActualOnOnePath")
public void testDiamondActualOnOnePath() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondActualOnOnePath/");
}
@TestMetadata("chainedTypeAliasRefinement")
public void testChainedTypeAliasRefinement() throws Exception {
runTest("testData/multiplatform/chainedTypeAliasRefinement/");
}
@TestMetadata("diamondDuplicateActuals")
public void testDiamondDuplicateActuals() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondDuplicateActuals/");
}
@TestMetadata("commonSealedWithPlatformInheritor")
public void testCommonSealedWithPlatformInheritor() throws Exception {
runTest("testData/multiplatform/commonSealedWithPlatformInheritor/");
}
@TestMetadata("diamondModuleDependency1")
public void testDiamondModuleDependency1() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondModuleDependency1/");
}
@TestMetadata("constructorsOfExpect")
public void testConstructorsOfExpect() throws Exception {
runTest("testData/multiplatform/constructorsOfExpect/");
}
@TestMetadata("diamondModuleDependency2")
public void testDiamondModuleDependency2() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondModuleDependency2/");
}
@TestMetadata("correctOverloadResolutionAmbiguity")
public void testCorrectOverloadResolutionAmbiguity() throws Exception {
runTest("testData/multiplatform/correctOverloadResolutionAmbiguity/");
}
@TestMetadata("diamondSeesTwoActuals")
public void testDiamondSeesTwoActuals() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/diamondSeesTwoActuals/");
}
@TestMetadata("defaultArguments")
public void testDefaultArguments() throws Exception {
runTest("testData/multiplatform/defaultArguments/");
}
@TestMetadata("duplicateActualsExplicit")
public void testDuplicateActualsExplicit() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/duplicateActualsExplicit/");
}
@TestMetadata("diamondActualInBottom")
public void testDiamondActualInBottom() throws Exception {
runTest("testData/multiplatform/diamondActualInBottom/");
}
@TestMetadata("duplicateActualsImplicit")
public void testDuplicateActualsImplicit() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/duplicateActualsImplicit/");
}
@TestMetadata("diamondActualOnOnePath")
public void testDiamondActualOnOnePath() throws Exception {
runTest("testData/multiplatform/diamondActualOnOnePath/");
}
@TestMetadata("duplicateActualsOneWeaklyIncompatible")
public void testDuplicateActualsOneWeaklyIncompatible() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/duplicateActualsOneWeaklyIncompatible/");
}
@TestMetadata("diamondDuplicateActuals")
public void testDiamondDuplicateActuals() throws Exception {
runTest("testData/multiplatform/diamondDuplicateActuals/");
}
@TestMetadata("diamondModuleDependency1")
public void testDiamondModuleDependency1() throws Exception {
runTest("testData/multiplatform/diamondModuleDependency1/");
}
@TestMetadata("diamondModuleDependency2")
public void testDiamondModuleDependency2() throws Exception {
runTest("testData/multiplatform/diamondModuleDependency2/");
}
@TestMetadata("diamondSeesTwoActuals")
public void testDiamondSeesTwoActuals() throws Exception {
runTest("testData/multiplatform/diamondSeesTwoActuals/");
}
@TestMetadata("differentKindsOfDependencies")
public void testDifferentKindsOfDependencies() throws Exception {
runTest("testData/multiplatform/differentKindsOfDependencies/");
}
@TestMetadata("duplicateActualsExplicit")
public void testDuplicateActualsExplicit() throws Exception {
runTest("testData/multiplatform/duplicateActualsExplicit/");
}
@TestMetadata("duplicateActualsImplicit")
public void testDuplicateActualsImplicit() throws Exception {
runTest("testData/multiplatform/duplicateActualsImplicit/");
}
@TestMetadata("duplicateActualsOneWeaklyIncompatible")
public void testDuplicateActualsOneWeaklyIncompatible() throws Exception {
runTest("testData/multiplatform/duplicateActualsOneWeaklyIncompatible/");
}
@TestMetadata("duplicateActualsOneWithStrongIncompatibility")
public void testDuplicateActualsOneWithStrongIncompatibility() throws Exception {
runTest("testData/multiplatform/duplicateActualsOneWithStrongIncompatibility/");
}
@TestMetadata("duplicateExpectsExplicit")
public void testDuplicateExpectsExplicit() throws Exception {
runTest("testData/multiplatform/duplicateExpectsExplicit/");
}
@TestMetadata("duplicateExpectsImplicit")
public void testDuplicateExpectsImplicit() throws Exception {
runTest("testData/multiplatform/duplicateExpectsImplicit/");
}
@TestMetadata("duplicateExpectsWithStrongIncompatibility")
public void testDuplicateExpectsWithStrongIncompatibility() throws Exception {
runTest("testData/multiplatform/duplicateExpectsWithStrongIncompatibility/");
}
@TestMetadata("enumFromCommonSerlializableSupertype")
public void testEnumFromCommonSerlializableSupertype() throws Exception {
runTest("testData/multiplatform/enumFromCommonSerlializableSupertype/");
}
@TestMetadata("expectActualLineMarkers")
public void testExpectActualLineMarkers() throws Exception {
runTest("testData/multiplatform/expectActualLineMarkers/");
}
@TestMetadata("extensionOnExpect")
public void testExtensionOnExpect() throws Exception {
runTest("testData/multiplatform/extensionOnExpect/");
}
@TestMetadata("hierarcicalActualization")
public void testHierarcicalActualization() throws Exception {
runTest("testData/multiplatform/hierarcicalActualization/");
}
@TestMetadata("incompleteActualization")
public void testIncompleteActualization() throws Exception {
runTest("testData/multiplatform/incompleteActualization/");
}
@TestMetadata("internalFromDependsOn")
public void testInternalFromDependsOn() throws Exception {
runTest("testData/multiplatform/internalFromDependsOn/");
}
@TestMetadata("internalFromDependsOnOfProduction")
public void testInternalFromDependsOnOfProduction() throws Exception {
runTest("testData/multiplatform/internalFromDependsOnOfProduction/");
}
@TestMetadata("internalFromProduction")
public void testInternalFromProduction() throws Exception {
runTest("testData/multiplatform/internalFromProduction/");
}
@TestMetadata("jsNameClash")
public void testJsNameClash() throws Exception {
runTest("testData/multiplatform/jsNameClash/");
}
@TestMetadata("jvmInlineValueClass")
public void testJvmInlineValueClass() throws Exception {
runTest("testData/multiplatform/jvmInlineValueClass/");
}
@TestMetadata("kt41218")
public void testKt41218() throws Exception {
runTest("testData/multiplatform/kt41218/");
}
@TestMetadata("kt44898")
public void testKt44898() throws Exception {
runTest("testData/multiplatform/kt44898/");
}
@TestMetadata("kt48291")
public void testKt48291() throws Exception {
runTest("testData/multiplatform/kt48291/");
}
@TestMetadata("ktij22295")
public void testKtij22295() throws Exception {
runTest("testData/multiplatform/ktij22295/");
}
@TestMetadata("ktij27523")
public void testKtij27523() throws Exception {
runTest("testData/multiplatform/ktij27523/");
}
@TestMetadata("lambdas")
public void testLambdas() throws Exception {
runTest("testData/multiplatform/lambdas/");
}
@TestMetadata("languageConstructions")
public void testLanguageConstructions() throws Exception {
runTest("testData/multiplatform/languageConstructions/");
}
@TestMetadata("multilevelParents")
public void testMultilevelParents() throws Exception {
runTest("testData/multiplatform/multilevelParents/");
}
@TestMetadata("multiplatformLibrary")
public void testMultiplatformLibrary() throws Exception {
runTest("testData/multiplatform/multiplatformLibrary/");
}
@TestMetadata("nativeStdlib")
public void testNativeStdlib() throws Exception {
runTest("testData/multiplatform/nativeStdlib/");
}
@TestMetadata("nativeTestLineMarker")
public void testNativeTestLineMarker() throws Exception {
runTest("testData/multiplatform/nativeTestLineMarker/");
}
@TestMetadata("overrideExpect")
public void testOverrideExpect() throws Exception {
runTest("testData/multiplatform/overrideExpect/");
}
@TestMetadata("overrideExpectWithCompositeType")
public void testOverrideExpectWithCompositeType() throws Exception {
runTest("testData/multiplatform/overrideExpectWithCompositeType/");
}
@TestMetadata("platformDependencyInCommon")
public void testPlatformDependencyInCommon() throws Exception {
runTest("testData/multiplatform/platformDependencyInCommon/");
}
@TestMetadata("platformSpecificChecksInCommon")
public void testPlatformSpecificChecksInCommon() throws Exception {
runTest("testData/multiplatform/platformSpecificChecksInCommon/");
}
@TestMetadata("qualifiedReceiver")
public void testQualifiedReceiver() throws Exception {
runTest("testData/multiplatform/qualifiedReceiver/");
}
@TestMetadata("recursiveTypes")
public void testRecursiveTypes() throws Exception {
runTest("testData/multiplatform/recursiveTypes/");
}
@TestMetadata("sealedInheritorsInComplexModuleStructure1")
public void testSealedInheritorsInComplexModuleStructure1() throws Exception {
runTest("testData/multiplatform/sealedInheritorsInComplexModuleStructure1/");
}
@TestMetadata("sealedInheritorsInComplexModuleStructure2")
public void testSealedInheritorsInComplexModuleStructure2() throws Exception {
runTest("testData/multiplatform/sealedInheritorsInComplexModuleStructure2/");
}
@TestMetadata("simple")
public void testSimple() throws Exception {
runTest("testData/multiplatform/simple/");
}
@TestMetadata("smartCastOnPropertyFromDependentModule")
public void testSmartCastOnPropertyFromDependentModule() throws Exception {
runTest("testData/multiplatform/smartCastOnPropertyFromDependentModule/");
}
@TestMetadata("supertypes")
public void testSupertypes() throws Exception {
runTest("testData/multiplatform/supertypes/");
}
@TestMetadata("transitiveDependencyOnCommonSourceSets")
public void testTransitiveDependencyOnCommonSourceSets() throws Exception {
runTest("testData/multiplatform/transitiveDependencyOnCommonSourceSets/");
}
@TestMetadata("typeAliasToExpectClassExplicitReference")
public void testTypeAliasToExpectClassExplicitReference() throws Exception {
runTest("testData/multiplatform/typeAliasToExpectClassExplicitReference/");
}
@TestMetadata("typeAliases")
public void testTypeAliases() throws Exception {
runTest("testData/multiplatform/typeAliases/");
}
@TestMetadata("typeParameters")
public void testTypeParameters() throws Exception {
runTest("testData/multiplatform/typeParameters/");
}
@TestMetadata("unresolvedInMultiplatformLibrary")
public void testUnresolvedInMultiplatformLibrary() throws Exception {
runTest("testData/multiplatform/unresolvedInMultiplatformLibrary/");
}
@TestMetadata("useCorrectBuiltIns")
public void testUseCorrectBuiltIns() throws Exception {
runTest("testData/multiplatform/useCorrectBuiltIns/");
}
@TestMetadata("weaklyIncompatibleActualInIntermediateModule")
public void testWeaklyIncompatibleActualInIntermediateModule() throws Exception {
runTest("testData/multiplatform/weaklyIncompatibleActualInIntermediateModule/");
}
@TestMetadata("whenExhaustivenessForSealed")
public void testWhenExhaustivenessForSealed() throws Exception {
runTest("testData/multiplatform/whenExhaustivenessForSealed/");
@TestMetadata("duplicateActualsOneWithStrongIncompatibility")
public void testDuplicateActualsOneWithStrongIncompatibility() throws Exception {
runTest("testData/multiplatform/ambiguousActuals/duplicateActualsOneWithStrongIncompatibility/");
}
}
}
@@ -1,3 +1,4 @@
// AMBIGUOUS_ACTUALS:
package sample
expect class <!LINE_MARKER("descr='Has actuals in bottom module'")!>A<!>
@@ -1,3 +1,4 @@
// AMBIGUOUS_ACTUALS:
package sample
expect class <!LINE_MARKER("descr='Has actuals in left module'")!>ActualizedInLeft<!>
@@ -1,3 +1,4 @@
// AMBIGUOUS_ACTUALS:
package sample
expect class <!LINE_MARKER("descr='Has actuals in [left, right] modules'; targets=[(text=left); (text=right)]")!>A<!> {
@@ -1,3 +1,4 @@
// AMBIGUOUS_ACTUALS: bottom, middle
package foo
expect class <!AMBIGUOUS_ACTUALS, LINE_MARKER("descr='Has actuals in [bottom, middle] modules'; targets=[(text=bottom); (text=middle)]")!>A<!>
@@ -1,5 +1,5 @@
// !RENDER_DIAGNOSTICS_MESSAGES
// AMBIGUOUS_ACTUALS: bottom, middle
package foo
expect class <!AMBIGUOUS_ACTUALS("Class 'A'; bottom, middle"), LINE_MARKER("descr='Has actuals in middle module'")!>A<!>
@@ -1,3 +1,4 @@
// AMBIGUOUS_ACTUALS:
package foo
expect fun <!LINE_MARKER("descr='Has actuals in [bottom, middle] modules'; targets=[(text=bottom); (text=middle)]")!>foo<!>(): Int
@@ -18,9 +18,10 @@ import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyFqnNameIndex
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelTypeAliasFqNameIndex
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isExpectDeclaration
@RequiresBackgroundThread(generateAssertion = false)
fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadAction { useScope }): Sequence<SmartPsiElementPointer<KtDeclaration>> {
fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadAction { useScope }, compatibleOnly: Boolean = true): Sequence<SmartPsiElementPointer<KtDeclaration>> {
val declaration = this
val scope = searchScope as? GlobalSearchScope ?: return emptySequence()
val containingClassOrObjectOrSelf = parentOfType<KtClassOrObject>(withSelf = true)
@@ -28,9 +29,9 @@ fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadActio
containingClassOrObjectOrSelf?.fqName?.let { fqName ->
val fqNameAsString = fqName.asString()
val targetDeclarations = KotlinFullClassNameIndex.getAllElements(fqNameAsString, project, scope) {
it.matchesWithExpect(containingClassOrObjectOrSelf)
it.matchesWithExpect(containingClassOrObjectOrSelf, compatibleOnly)
} + KotlinTopLevelTypeAliasFqNameIndex.getAllElements(fqNameAsString, project, scope) {
it.matchesWithExpect(containingClassOrObjectOrSelf)
it.matchesWithExpect(containingClassOrObjectOrSelf, compatibleOnly)
}
return targetDeclarations.mapNotNull { targetDeclaration ->
@@ -39,10 +40,13 @@ fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadActio
is KtConstructor<*> -> {
if (targetDeclaration is KtClass) {
val primaryConstructor = targetDeclaration.primaryConstructor
if (primaryConstructor?.matchesWithExpect(declaration) == true) {
if (primaryConstructor?.matchesWithExpect(declaration, compatibleOnly) == true) {
primaryConstructor
} else {
val secondaryConstructor = targetDeclaration.secondaryConstructors.find { it.matchesWithExpect(declaration) }
val secondaryConstructor = targetDeclaration.secondaryConstructors.find { it.matchesWithExpect(
declaration,
compatibleOnly
) }
if (secondaryConstructor != null) secondaryConstructor else if (declaration.valueParameters.isEmpty()) targetDeclaration else null
}
} else null
@@ -50,7 +54,7 @@ fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadActio
is KtNamedDeclaration ->
when (targetDeclaration) {
is KtClassOrObject -> targetDeclaration.declarations.firstOrNull {
it is KtNamedDeclaration && it.name == declaration.name && it.matchesWithExpect(declaration)
it is KtNamedDeclaration && it.name == declaration.name && it.matchesWithExpect(declaration, compatibleOnly)
}
else -> null
}
@@ -65,11 +69,11 @@ fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadActio
val topLevelFqName = packageFqName.child(Name.identifier(name)).asString()
return when (declaration) {
is KtNamedFunction -> KotlinTopLevelFunctionFqnNameIndex.getAllElements(topLevelFqName, project, scope) {
it.matchesWithExpect(declaration)
it.matchesWithExpect(declaration, compatibleOnly)
}
is KtProperty -> KotlinTopLevelPropertyFqnNameIndex.getAllElements(topLevelFqName, project, scope) {
it.matchesWithExpect(declaration)
it.matchesWithExpect(declaration, compatibleOnly)
}
else -> emptySequence()
@@ -77,10 +81,19 @@ fun KtDeclaration.findAllActualForExpect(searchScope: SearchScope = runReadActio
}
@OptIn(KaExperimentalApi::class)
private fun KtDeclaration.matchesWithExpect(expectDeclaration: KtDeclaration): Boolean {
private fun KtDeclaration.matchesWithExpect(expectDeclaration: KtDeclaration, compatibleOnly: Boolean): Boolean {
val declaration = this
return declaration.isEffectivelyActual() && analyze(declaration) {
if (compatibleOnly) {
if (!declaration.isEffectivelyActual()) {
return false
}
} else {
if (declaration.isExpectDeclaration()) {
return false
}
}
return analyze(declaration) {
val symbol: KaDeclarationSymbol = declaration.symbol
return symbol.getExpectsForActual().any { it.psi == expectDeclaration }
symbol.getExpectsForActual().any { it.psi == expectDeclaration }
}
}
@@ -1131,7 +1131,8 @@ private fun assembleWorkspace(): TWorkspace = workspace(KotlinPluginMode.K1) {
}
testClass<AbstractMultiplatformAnalysisTest> {
model("multiplatform", isRecursive = false, pattern = DIRECTORY)
model("multiplatform", isRecursive = false, pattern = DIRECTORY, excludedDirectories = listOf("ambiguousActuals"))
model("multiplatform/ambiguousActuals", isRecursive = false, pattern = DIRECTORY)
}
testClass<AbstractBytecodeToolWindowTest> {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.idea.k2.quickfix.tests.AbstractK2MultiFileQuickFixTe
import org.jetbrains.kotlin.idea.k2.quickfix.tests.AbstractK2QuickFixTest
import org.jetbrains.kotlin.testGenerator.model.*
import org.jetbrains.kotlin.testGenerator.model.GroupCategory.*
import org.jetbrains.kotlin.testGenerator.model.Patterns.DIRECTORY
internal fun MutableTWorkspace.generateK2InspectionTests() {
@@ -170,6 +171,10 @@ internal fun MutableTWorkspace.generateK2InspectionTests() {
model("code-insight/inspections-k2/tests/testData/multiFileInspectionsLocal", pattern = pattern)
}
testClass<AbstractK2AmbiguousActualsTest> {
model("${idea}/multiplatform/ambiguousActuals", pattern = DIRECTORY, isRecursive = false)
}
testClass<AbstractK2ActualExpectTest> {
val pattern = Patterns.forRegex("^([\\w\\-_]+)\\.kt$")
model("code-insight/inspections-k2/tests/testData/multiplatform/actualExpect/", pattern = pattern, isRecursive = false)