diff --git a/idea/idea-fir/tests/org/jetbrains/kotlin/shortenRefs/FirShortenRefsTestGenerated.java b/idea/idea-fir/tests/org/jetbrains/kotlin/shortenRefs/FirShortenRefsTestGenerated.java index dfd16ca314a..26e6b191d34 100644 --- a/idea/idea-fir/tests/org/jetbrains/kotlin/shortenRefs/FirShortenRefsTestGenerated.java +++ b/idea/idea-fir/tests/org/jetbrains/kotlin/shortenRefs/FirShortenRefsTestGenerated.java @@ -87,6 +87,44 @@ public class FirShortenRefsTestGenerated extends AbstractFirShortenRefsTest { } } + @TestMetadata("idea/testData/shortenRefsFir/quailfiers") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Quailfiers extends AbstractFirShortenRefsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithMuting, this, testDataFilePath); + } + + public void testAllFilesPresentInQuailfiers() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/shortenRefsFir/quailfiers"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("AlreadyImportedNestedType.kt") + public void testAlreadyImportedNestedType() throws Exception { + runTest("idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt"); + } + + @TestMetadata("NestedTypeInSameFile.kt") + public void testNestedTypeInSameFile() throws Exception { + runTest("idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt"); + } + + @TestMetadata("NotImportedNestedType.kt") + public void testNotImportedNestedType() throws Exception { + runTest("idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt"); + } + + @TestMetadata("NotImportedTopLevelType.kt") + public void testNotImportedTopLevelType() throws Exception { + runTest("idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt"); + } + + @TestMetadata("TopLevelTypeInSameFile.kt") + public void testTopLevelTypeInSameFile() throws Exception { + runTest("idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt"); + } + } + @TestMetadata("idea/testData/shortenRefsFir/types") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirReferenceShortener.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirReferenceShortener.kt index c1520243ac8..b5a691afe5f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirReferenceShortener.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirReferenceShortener.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirResolvedImport import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.ScopeSession @@ -179,8 +180,8 @@ internal class KtFirReferenceShortener( } private fun collectTypeIfNeedsToBeShortened(wholeClassifierId: ClassId, wholeTypeElement: KtUserType) { - val allClassIds = generateSequence(wholeClassifierId) { it.outerClassId } - val allTypeElements = generateSequence(wholeTypeElement) { it.qualifier } + val allClassIds = wholeClassifierId.outerClassesWithSelf + val allTypeElements = wholeTypeElement.qualifiersWithSelf val positionScopes = findScopesAtPosition(wholeTypeElement, namesToImport) ?: return @@ -218,7 +219,7 @@ internal class KtFirReferenceShortener( } private inner class CallsCollectingVisitor( - private val namesToImport: List, + private val namesToImport: MutableList, private val callsToShorten: MutableList ) : FirVisitorVoid() { override fun visitElement(element: FirElement) { @@ -258,10 +259,63 @@ internal class KtFirReferenceShortener( } } + + override fun visitResolvedQualifier(resolvedQualifier: FirResolvedQualifier) { + super.visitResolvedQualifier(resolvedQualifier) + + val wholeClassQualifier = resolvedQualifier.classId ?: return + val wholeQualifierElement = when (val qualifierPsi = resolvedQualifier.psi) { + is KtDotQualifiedExpression -> qualifierPsi + is KtNameReferenceExpression -> qualifierPsi.parent as? KtDotQualifiedExpression ?: return + else -> return + } + + collectQualifierIfNeedsToBeShortened(wholeClassQualifier, wholeQualifierElement) + } + + private fun collectQualifierIfNeedsToBeShortened(wholeClassQualifier: ClassId, wholeQualifierElement: KtDotQualifiedExpression) { + val positionScopes = findScopesAtPosition(wholeQualifierElement, namesToImport) ?: return + + val allClassIds = wholeClassQualifier.outerClassesWithSelf + val allQualifiers = wholeQualifierElement.qualifiersWithSelf + + for ((classId, qualifier) in allClassIds.zip(allQualifiers)) { + val firstFoundClass = findFirstClassifierInScopesByName(positionScopes, classId.shortClassName)?.classId + + if (firstFoundClass == classId) { + addElementToShorten(qualifier) + return + } + } + + val (mostTopLevelClassId, mostTopLevelQualifier) = allClassIds.zip(allQualifiers).last() + val availableClassifier = findFirstClassifierInScopesByName(positionScopes, mostTopLevelClassId.shortClassName) + + check(availableClassifier?.classId != mostTopLevelClassId) { "This should not be true" } + + if (availableClassifier == null || availableClassifier.isFromStarOrPackageImport) { + addElementToImportAndShorten(mostTopLevelClassId.asSingleFqName(), mostTopLevelQualifier) + } + } + private fun addElementToShorten(element: KtDotQualifiedExpression) { callsToShorten.add(element) } + + private fun addElementToImportAndShorten(nameToImport: FqName, element: KtDotQualifiedExpression) { + namesToImport.add(nameToImport) + callsToShorten.add(element) + } } + + private val ClassId.outerClassesWithSelf: Sequence + get() = generateSequence(this) { it.outerClassId } + + private val KtUserType.qualifiersWithSelf: Sequence + get() = generateSequence(this) { it.qualifier } + + private val KtDotQualifiedExpression.qualifiersWithSelf: Sequence + get() = generateSequence(this) { it.receiverExpression as? KtDotQualifiedExpression } } private class ShortenCommandImpl( diff --git a/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.dependency.kt b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.dependency.kt new file mode 100644 index 00000000000..007010a3e40 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.dependency.kt @@ -0,0 +1,5 @@ +package dependency + +class T { + class TT +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt new file mode 100644 index 00000000000..2f926b6c4ed --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt @@ -0,0 +1,12 @@ +// FIR_COMPARISON +package test + +import dependency.T +import dependency.T.TT + +fun usage() { + + dependency.T.TT + T.TT + +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt.after b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt.after new file mode 100644 index 00000000000..711037bf58a --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/AlreadyImportedNestedType.kt.after @@ -0,0 +1,12 @@ +// FIR_COMPARISON +package test + +import dependency.T +import dependency.T.TT + +fun usage() { + + TT + TT + +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt b/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt new file mode 100644 index 00000000000..b9d3dfb2a62 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt @@ -0,0 +1,10 @@ +// FIR_COMPARISON +package test + +class A { + class AA +} + +fun usage() { + test.A.AA +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt.after b/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt.after new file mode 100644 index 00000000000..3c7751dfcb1 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NestedTypeInSameFile.kt.after @@ -0,0 +1,10 @@ +// FIR_COMPARISON +package test + +class A { + class AA +} + +fun usage() { + A.AA +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.dependency.kt b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.dependency.kt new file mode 100644 index 00000000000..007010a3e40 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.dependency.kt @@ -0,0 +1,5 @@ +package dependency + +class T { + class TT +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt new file mode 100644 index 00000000000..da8709a0004 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt @@ -0,0 +1,6 @@ +// FIR_COMPARISON +package test + +fun usage() { + dependency.T.TT +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt.after b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt.after new file mode 100644 index 00000000000..dc5146b7aca --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedNestedType.kt.after @@ -0,0 +1,8 @@ +// FIR_COMPARISON +package test + +import dependency.T + +fun usage() { + T.TT +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.dependency.kt b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.dependency.kt new file mode 100644 index 00000000000..fc6fb4fa1c8 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.dependency.kt @@ -0,0 +1,3 @@ +package dependency + +class T \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt new file mode 100644 index 00000000000..d8c9f92d525 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt @@ -0,0 +1,6 @@ +// FIR_COMPARISON +package test + +fun usage() { + dependency.T +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt.after b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt.after new file mode 100644 index 00000000000..9c1bfa1567c --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/NotImportedTopLevelType.kt.after @@ -0,0 +1,8 @@ +// FIR_COMPARISON +package test + +import dependency.T + +fun usage() { + T +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt b/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt new file mode 100644 index 00000000000..da3f7bee2db --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt @@ -0,0 +1,8 @@ +// FIR_COMPARISON +package test + +class A + +fun usage() { + test.A +} \ No newline at end of file diff --git a/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt.after b/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt.after new file mode 100644 index 00000000000..30eb3797e03 --- /dev/null +++ b/idea/testData/shortenRefsFir/quailfiers/TopLevelTypeInSameFile.kt.after @@ -0,0 +1,8 @@ +// FIR_COMPARISON +package test + +class A + +fun usage() { + A +} \ No newline at end of file