From f2d513a39cbcb298f5e662495088b21830614285 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Fri, 5 Apr 2019 12:40:35 +0700 Subject: [PATCH] Fix: Unstable IDE navigation to `expect`/`actual` symbols in stdlib Issue #KT-30790 Fixed --- .../navigation/SourceNavigationHelper.kt | 5 +- .../LightNavigateToStdlibSourceTest.kt | 46 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/SourceNavigationHelper.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/SourceNavigationHelper.kt index a66dfc57676..39c18d90967 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/SourceNavigationHelper.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/SourceNavigationHelper.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.debugText.getDebugText -import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult object SourceNavigationHelper { @@ -191,8 +190,8 @@ object SourceNavigationHelper { index: StringStubIndexExtension ): T? { val classFqName = entity.fqName ?: return null - return targetScopes(entity, navigationKind).firstNotNullResult { - index.get(classFqName.asString(), entity.project, it).firstOrNull() + return targetScopes(entity, navigationKind).firstNotNullResult { scope -> + index.get(classFqName.asString(), entity.project, scope).sortedBy { it.isExpectDeclaration() }.firstOrNull() } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/LightNavigateToStdlibSourceTest.kt b/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/LightNavigateToStdlibSourceTest.kt index 9ef0a6d292f..85b6beebcd9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/LightNavigateToStdlibSourceTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/navigation/LightNavigateToStdlibSourceTest.kt @@ -5,9 +5,14 @@ package org.jetbrains.kotlin.idea.decompiler.navigation +import com.intellij.psi.PsiElement import junit.framework.TestCase import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.test.* +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtTypeAlias +import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier class LightNavigateToStdlibSourceTest : KotlinLightCodeInsightFixtureTestCase() { @ProjectDescriptorKind(JDK_AND_MULTIPLATFORM_STDLIB_WITH_SOURCES) @@ -26,6 +31,35 @@ class LightNavigateToStdlibSourceTest : KotlinLightCodeInsightFixtureTestCase() ) } + @ProjectDescriptorKind(KOTLIN_JVM_WITH_STDLIB_SOURCES) + fun testNavigateToJVMActualClassDeclarations() { + val checkedTypes = listOf( + // abstract actual classes in JVM: + "AbstractMutableCollection", + "AbstractMutableList", + "AbstractMutableSet", + "AbstractMutableMap", + + // actual typealiases in JVM: + "ArrayList", + "HashSet" + ) + + for (type in checkedTypes) { + doTest("fun some() { val collection: $type? = null }") { navigationElement -> + TestCase.assertTrue( + "$navigationElement is not instance of ${KtClassOrObject::class} or ${KtTypeAlias::class}", + navigationElement is KtClassOrObject || navigationElement is KtTypeAlias + ) + + TestCase.assertTrue( + "$navigationElement is not actual declaration", + (navigationElement as KtModifierListOwner).hasActualModifier() + ) + } + } + } + @ProjectDescriptorKind(KOTLIN_JVM_WITH_STDLIB_SOURCES) fun testRefToPrintlnWithJVM() { doTest( @@ -57,13 +91,19 @@ class LightNavigateToStdlibSourceTest : KotlinLightCodeInsightFixtureTestCase() override fun getProjectDescriptor() = getProjectDescriptorFromAnnotation() - private fun doTest(text: String, sourceFileName: String) { + private fun doTest(text: String, sourceFileName: String) = doTest(text) { navigationElement -> + // by default check by source file name + TestCase.assertEquals(sourceFileName, navigationElement.containingFile.name) + } + + private fun doTest(text: String, checker: (PsiElement) -> Unit) { myFixture.configureByText(KotlinFileType.INSTANCE, text) val ref = file.findReferenceAt(editor.caretModel.offset) val resolve = ref!!.resolve() val navigationElement = resolve!!.navigationElement - TestCase.assertEquals(sourceFileName, navigationElement.containingFile.name) + checker(navigationElement) } -} \ No newline at end of file +} +