FIR IDE: Implement shortening and import for type qualifiers

This commit is contained in:
Roman Golyshev
2020-12-28 13:22:03 +03:00
committed by Space
parent 0e271b72c7
commit e265a78a33
15 changed files with 196 additions and 3 deletions
@@ -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)
@@ -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<FqName>,
private val namesToImport: MutableList<FqName>,
private val callsToShorten: MutableList<KtDotQualifiedExpression>
) : 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<ClassId>
get() = generateSequence(this) { it.outerClassId }
private val KtUserType.qualifiersWithSelf: Sequence<KtUserType>
get() = generateSequence(this) { it.qualifier }
private val KtDotQualifiedExpression.qualifiersWithSelf: Sequence<KtDotQualifiedExpression>
get() = generateSequence(this) { it.receiverExpression as? KtDotQualifiedExpression }
}
private class ShortenCommandImpl(
@@ -0,0 +1,5 @@
package dependency
class T {
class TT
}
@@ -0,0 +1,12 @@
// FIR_COMPARISON
package test
import dependency.T
import dependency.T.TT
fun usage() {
<selection>
dependency.T.TT
T.TT
</selection>
}
@@ -0,0 +1,12 @@
// FIR_COMPARISON
package test
import dependency.T
import dependency.T.TT
fun usage() {
TT
TT
}
@@ -0,0 +1,10 @@
// FIR_COMPARISON
package test
class A {
class AA
}
fun usage() {
<selection>test.A.AA</selection>
}
@@ -0,0 +1,10 @@
// FIR_COMPARISON
package test
class A {
class AA
}
fun usage() {
A.AA
}
@@ -0,0 +1,5 @@
package dependency
class T {
class TT
}
@@ -0,0 +1,6 @@
// FIR_COMPARISON
package test
fun usage() {
<selection>dependency.T.TT</selection>
}
@@ -0,0 +1,8 @@
// FIR_COMPARISON
package test
import dependency.T
fun usage() {
T.TT
}
@@ -0,0 +1,3 @@
package dependency
class T
@@ -0,0 +1,6 @@
// FIR_COMPARISON
package test
fun usage() {
<selection>dependency.T</selection>
}
@@ -0,0 +1,8 @@
// FIR_COMPARISON
package test
import dependency.T
fun usage() {
T
}
@@ -0,0 +1,8 @@
// FIR_COMPARISON
package test
class A
fun usage() {
<selection>test.A</selection>
}
@@ -0,0 +1,8 @@
// FIR_COMPARISON
package test
class A
fun usage() {
A
}