[FIR IDE] Improve handling aliased types in KtFirImportOptimizer

This commit is contained in:
Roman Golyshev
2021-09-03 22:31:13 +03:00
parent a06fc20680
commit bb3f55dec2
6 changed files with 69 additions and 18 deletions
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.utils.isOperator import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -171,12 +170,12 @@ internal class KtFirImportOptimizer(
val mostOuterTypeQualifier = generateSequence(qualifier) { it.outerTypeQualifier }.last() val mostOuterTypeQualifier = generateSequence(qualifier) { it.outerTypeQualifier }.last()
if (mostOuterTypeQualifier.isQualified) return if (mostOuterTypeQualifier.isQualified) return
saveType(mostOuterTypeQualifier.classId) saveType(mostOuterTypeQualifier)
} }
private fun saveType(classId: ClassId) { private fun saveType(qualifier: TypeQualifier) {
val importableName = classId.asSingleFqName() val importableName = qualifier.referencedClassId.asSingleFqName()
val referencedByName = classId.shortClassName val referencedByName = qualifier.referencedByName
saveReferencedItem(importableName, referencedByName) saveReferencedItem(importableName, referencedByName)
} }
@@ -230,7 +229,14 @@ private val FirFunctionCall.isInvokeOperatorImplicitCall: Boolean
* to correctly reason about long qualifiers. * to correctly reason about long qualifiers.
*/ */
private sealed interface TypeQualifier { private sealed interface TypeQualifier {
val classId: ClassId val referencedClassId: ClassId
/**
* Type can be imported with alias, and thus can be referenced by the name different from its actual name.
*
* We cannot use [ClassId.getShortClassName] for this, since it is not affected by the alias.
*/
val referencedByName: Name
/** /**
* Must be `true` if the PSI qualifier is itself qualified with the package or some other type, and `false` otherwise. * Must be `true` if the PSI qualifier is itself qualified with the package or some other type, and `false` otherwise.
@@ -246,39 +252,48 @@ private sealed interface TypeQualifier {
val outerTypeQualifier: TypeQualifier? val outerTypeQualifier: TypeQualifier?
private class KtDotExpressionTypeQualifier( private class KtDotExpressionTypeQualifier(
override val classId: ClassId, override val referencedClassId: ClassId,
private val qualifier: KtElement, qualifier: KtElement,
) : TypeQualifier { ) : TypeQualifier {
init { private val dotQualifier: KtDotQualifiedExpression? = qualifier as? KtDotQualifiedExpression
require(qualifier is KtDotQualifiedExpression || qualifier is KtNameReferenceExpression) {
"Unexpected type of qualifier: ${qualifier::class}" private val typeNameReference: KtNameReferenceExpression = when (qualifier) {
} is KtDotQualifiedExpression -> qualifier.selectorExpression as? KtNameReferenceExpression
} is KtNameReferenceExpression -> qualifier
else -> null
} ?: error("Cannot get referenced name from '${qualifier.text}'")
override val referencedByName: Name
get() = typeNameReference.getReferencedNameAsName()
override val isQualified: Boolean override val isQualified: Boolean
get() = qualifier is KtDotQualifiedExpression get() = dotQualifier != null
override val outerTypeQualifier: TypeQualifier? override val outerTypeQualifier: TypeQualifier?
get() { get() {
val outerClassId = classId.outerClassId ?: return null val outerClassId = referencedClassId.outerClassId ?: return null
val outerQualifier = (qualifier as? KtDotQualifiedExpression)?.receiverExpression ?: return null val outerQualifier = dotQualifier?.receiverExpression ?: return null
return KtDotExpressionTypeQualifier(outerClassId, outerQualifier) return KtDotExpressionTypeQualifier(outerClassId, outerQualifier)
} }
} }
private class KtUserTypeQualifier( private class KtUserTypeQualifier(
override val classId: ClassId, override val referencedClassId: ClassId,
private val qualifier: KtUserType, private val qualifier: KtUserType,
) : TypeQualifier { ) : TypeQualifier {
override val referencedByName: Name
get() = qualifier.referenceExpression?.getReferencedNameAsName()
?: error("Cannot get referenced name from '${qualifier.text}'")
override val isQualified: Boolean override val isQualified: Boolean
get() = qualifier.qualifier != null get() = qualifier.qualifier != null
override val outerTypeQualifier: TypeQualifier? override val outerTypeQualifier: TypeQualifier?
get() { get() {
val outerClassId = classId.outerClassId ?: return null val outerClassId = referencedClassId.outerClassId ?: return null
val outerQualifier = qualifier.qualifier ?: return null val outerQualifier = qualifier.qualifier ?: return null
return KtUserTypeQualifier(outerClassId, outerQualifier) return KtUserTypeQualifier(outerClassId, outerQualifier)
@@ -0,0 +1 @@
dependency.Bar as BarAlias
@@ -0,0 +1,12 @@
// FILE: main.kt
package test
import dependency.Bar
import dependency.Bar as BarAlias
fun usage(p: Bar) {}
// FILE: dependency.kt
package dependency
class Bar
@@ -0,0 +1,11 @@
// FILE: main.kt
package test
import dependency.Bar as BarAlias
fun usage(p: BarAlias) {}
// FILE: dependency.kt
package dependency
class Bar
@@ -24,12 +24,24 @@ public class HLImportOptimizerTestGenerated extends AbstractHLImportOptimizerTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/components/importOptimizer"), Pattern.compile("^([^.]+)\\.kt$"), null, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/components/importOptimizer"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
} }
@Test
@TestMetadata("unusedAliasedTypeImport.kt")
public void testUnusedAliasedTypeImport() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/importOptimizer/unusedAliasedTypeImport.kt");
}
@Test @Test
@TestMetadata("unusedFunctionImports.kt") @TestMetadata("unusedFunctionImports.kt")
public void testUnusedFunctionImports() throws Exception { public void testUnusedFunctionImports() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/importOptimizer/unusedFunctionImports.kt"); runTest("idea/idea-frontend-fir/testData/components/importOptimizer/unusedFunctionImports.kt");
} }
@Test
@TestMetadata("usedAliasedTypeImport.kt")
public void testUsedAliasedTypeImport() throws Exception {
runTest("idea/idea-frontend-fir/testData/components/importOptimizer/usedAliasedTypeImport.kt");
}
@Test @Test
@TestMetadata("usedFunctionImport.kt") @TestMetadata("usedFunctionImport.kt")
public void testUsedFunctionImport() throws Exception { public void testUsedFunctionImport() throws Exception {