FIR IDE: Fix resolving of nested types in type references

This commit is contained in:
Roman Golyshev
2020-11-27 18:09:49 +03:00
parent 94a5379631
commit f50480d258
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.calls.SyntheticPropertySymbol
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.idea.fir.*
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFir
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
@@ -141,7 +143,10 @@ internal object FirReferenceResolveHelper {
when (fir) {
is FirResolvedTypeRef -> {
if (expression.isPartOfUserTypeRefQualifier()) {
return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = true))
val typeQualifier = findPossibleTypeQualifier(expression, fir)?.toTargetPsi(session, symbolBuilder)
val typeOrPackageQualifier = typeQualifier ?: getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = true)
return listOfNotNull(typeOrPackageQualifier)
}
return listOfNotNull(fir.toTargetSymbol(session, symbolBuilder))
}
@@ -274,4 +279,35 @@ internal object FirReferenceResolveHelper {
}
}
}
private fun findPossibleTypeQualifier(qualifier: KtSimpleNameExpression, wholeTypeFir: FirResolvedTypeRef): ClassId? {
val qualifierToResolve = qualifier.parent as KtUserType
// FIXME make it work with generics in functional types (like () -> AA.BB<CC, AA.DD>)
val wholeType = (wholeTypeFir.psi as KtTypeReference).typeElement?.unwrapNullable() as? KtUserType ?: return null
val qualifiersToDrop = countQualifiersToDrop(wholeType, qualifierToResolve)
return wholeTypeFir.type.classId?.dropLastNestedClasses(qualifiersToDrop)
}
/**
* @return class id without [classesToDrop] last nested classes, or `null` if [classesToDrop] is too big.
*
* Example: `foo.bar.Baz.Inner` with 1 dropped class is `foo.bar.Baz`, and with 2 dropped class is `null`.
*/
private fun ClassId.dropLastNestedClasses(classesToDrop: Int) = generateSequence(this) { it.outerClassId }.drop(classesToDrop).firstOrNull()
/**
* @return How many qualifiers needs to be dropped from [wholeType] to get [nestedType].
*
* Example: to get `foo.bar` from `foo.bar.Baz.Inner`, you need to drop 2 qualifiers (`Inner` and `Baz`).
*/
private fun countQualifiersToDrop(wholeType: KtUserType, nestedType: KtUserType): Int {
val qualifierIndex = generateSequence(wholeType) { it.qualifier }.indexOf(nestedType)
require(qualifierIndex != -1) { "Whole type $wholeType should contain $nestedType, but it didn't" }
return qualifierIndex
}
private tailrec fun KtTypeElement.unwrapNullable(): KtTypeElement? {
return if (this is KtNullableType) innerType?.unwrapNullable() else this
}
}