[FIR IDE] Improve imports analysis for complex type qualifiers
This commit is contained in:
+101
-21
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.FirElement
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
|
import org.jetbrains.kotlin.fir.analysis.checkers.classKind
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
@@ -19,21 +18,18 @@ import org.jetbrains.kotlin.fir.psi
|
|||||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
|
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.symbols.impl.FirClassLikeSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.classId
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirFile
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirFile
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtImportOptimizer
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtImportOptimizer
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtImportOptimizerResult
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtImportOptimizerResult
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.parentOrNull
|
import org.jetbrains.kotlin.name.parentOrNull
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
|
||||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
|
||||||
import org.jetbrains.kotlin.psi.KtUserType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability
|
import org.jetbrains.kotlin.psi.psiUtil.unwrapNullability
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
|
||||||
@@ -133,10 +129,9 @@ internal class KtFirImportOptimizer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
|
private fun processTypeRef(resolvedTypeRef: FirResolvedTypeRef) {
|
||||||
if (!resolvedTypeRef.canBeReferencedByImport()) return
|
val wholeQualifier = TypeQualifier.createFor(resolvedTypeRef) ?: return
|
||||||
|
|
||||||
val classSymbol = resolvedTypeRef.toClassLikeSymbol(firSession) ?: return
|
processTypeQualifier(wholeQualifier)
|
||||||
saveType(classSymbol)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processResolvedCallableReference(resolvedCallableReference: FirResolvedCallableReference) {
|
private fun processResolvedCallableReference(resolvedCallableReference: FirResolvedCallableReference) {
|
||||||
@@ -146,13 +141,20 @@ internal class KtFirImportOptimizer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processResolvedQualifier(resolvedQualifier: FirResolvedQualifier) {
|
private fun processResolvedQualifier(resolvedQualifier: FirResolvedQualifier) {
|
||||||
val qualifierSymbol = resolvedQualifier.symbol ?: return
|
val wholeQualifier = TypeQualifier.createFor(resolvedQualifier) ?: return
|
||||||
|
|
||||||
saveType(qualifierSymbol)
|
processTypeQualifier(wholeQualifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveType(symbol: FirClassLikeSymbol<*>) {
|
private fun processTypeQualifier(qualifier: TypeQualifier) {
|
||||||
usedImports += symbol.classId.asSingleFqName()
|
val mostOuterTypeQualifier = generateSequence(qualifier) { it.outerTypeQualifier }.last()
|
||||||
|
if (mostOuterTypeQualifier.isQualified) return
|
||||||
|
|
||||||
|
saveType(mostOuterTypeQualifier.classId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveType(classId: ClassId) {
|
||||||
|
usedImports += classId.asSingleFqName()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveCallable(symbol: FirCallableSymbol<*>) {
|
private fun saveCallable(symbol: FirCallableSymbol<*>) {
|
||||||
@@ -186,12 +188,90 @@ internal class KtFirImportOptimizer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirResolvedTypeRef.canBeReferencedByImport(): Boolean {
|
|
||||||
val wholeTypeReference = psi as? KtTypeReference ?: return false
|
|
||||||
|
|
||||||
val wholeTypeElement = wholeTypeReference.typeElement?.unwrapNullability() as? KtUserType ?: return false
|
|
||||||
return wholeTypeElement.qualifier == null
|
|
||||||
}
|
|
||||||
|
|
||||||
private val FirQualifiedAccessExpression.isFullyQualified: Boolean
|
private val FirQualifiedAccessExpression.isFullyQualified: Boolean
|
||||||
get() = explicitReceiver is FirResolvedQualifier
|
get() = explicitReceiver is FirResolvedQualifier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper abstraction to navigate through qualified FIR elements - we have to match [ClassId] and PSI qualifier pair
|
||||||
|
* to correctly reason about long qualifiers.
|
||||||
|
*/
|
||||||
|
private sealed interface TypeQualifier {
|
||||||
|
val classId: ClassId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be `true` if the PSI qualifier is itself qualified with the package or some other type, and `false` otherwise.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* foo.bar.Baz -> true
|
||||||
|
* Baz.Type -> true
|
||||||
|
* Baz -> false
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
val isQualified: Boolean
|
||||||
|
|
||||||
|
val outerTypeQualifier: TypeQualifier?
|
||||||
|
|
||||||
|
private class KtDotExpressionTypeQualifier(
|
||||||
|
override val classId: ClassId,
|
||||||
|
private val qualifier: KtElement,
|
||||||
|
) : TypeQualifier {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(qualifier is KtDotQualifiedExpression || qualifier is KtNameReferenceExpression) {
|
||||||
|
"Unexpected type of qualifier: ${qualifier::class}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isQualified: Boolean
|
||||||
|
get() = qualifier is KtDotQualifiedExpression
|
||||||
|
|
||||||
|
override val outerTypeQualifier: TypeQualifier?
|
||||||
|
get() {
|
||||||
|
val outerClassId = classId.outerClassId ?: return null
|
||||||
|
val outerQualifier = (qualifier as? KtDotQualifiedExpression)?.receiverExpression ?: return null
|
||||||
|
|
||||||
|
return KtDotExpressionTypeQualifier(outerClassId, outerQualifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KtUserTypeQualifier(
|
||||||
|
override val classId: ClassId,
|
||||||
|
private val qualifier: KtUserType,
|
||||||
|
) : TypeQualifier {
|
||||||
|
|
||||||
|
override val isQualified: Boolean
|
||||||
|
get() = qualifier.qualifier != null
|
||||||
|
|
||||||
|
override val outerTypeQualifier: TypeQualifier?
|
||||||
|
get() {
|
||||||
|
val outerClassId = classId.outerClassId ?: return null
|
||||||
|
val outerQualifier = qualifier.qualifier ?: return null
|
||||||
|
|
||||||
|
return KtUserTypeQualifier(outerClassId, outerQualifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun createFor(qualifier: FirResolvedQualifier): TypeQualifier? {
|
||||||
|
val wholeClassId = qualifier.classId ?: return null
|
||||||
|
val psi = qualifier.psi as? KtExpression ?: return null
|
||||||
|
|
||||||
|
val wholeQualifier = when (psi) {
|
||||||
|
is KtDotQualifiedExpression -> psi
|
||||||
|
is KtNameReferenceExpression -> psi.getDotQualifiedExpressionForSelector() ?: psi
|
||||||
|
else -> psi
|
||||||
|
}
|
||||||
|
|
||||||
|
return KtDotExpressionTypeQualifier(wholeClassId, wholeQualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFor(typeRef: FirResolvedTypeRef): TypeQualifier? {
|
||||||
|
val wholeClassId = typeRef.type.classId ?: return null
|
||||||
|
val psi = typeRef.psi as? KtTypeReference ?: return null
|
||||||
|
|
||||||
|
val wholeUserType = psi.typeElement?.unwrapNullability() as? KtUserType ?: return null
|
||||||
|
|
||||||
|
return KtUserTypeQualifier(wholeClassId, wholeUserType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -635,7 +635,7 @@ private fun KtUserType.hasFakeRootPrefix(): Boolean =
|
|||||||
private fun KtDotQualifiedExpression.hasFakeRootPrefix(): Boolean =
|
private fun KtDotQualifiedExpression.hasFakeRootPrefix(): Boolean =
|
||||||
(receiverExpression as? KtNameReferenceExpression)?.getReferencedName() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
|
(receiverExpression as? KtNameReferenceExpression)?.getReferencedName() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
|
||||||
|
|
||||||
private fun KtElement.getDotQualifiedExpressionForSelector(): KtDotQualifiedExpression? =
|
internal fun KtElement.getDotQualifiedExpressionForSelector(): KtDotQualifiedExpression? =
|
||||||
getQualifiedExpressionForSelector() as? KtDotQualifiedExpression
|
getQualifiedExpressionForSelector() as? KtDotQualifiedExpression
|
||||||
|
|
||||||
private fun KtDotQualifiedExpression.deleteQualifier(): KtExpression? {
|
private fun KtDotQualifiedExpression.deleteQualifier(): KtExpression? {
|
||||||
|
|||||||
Reference in New Issue
Block a user