diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/references/FirReferenceResolveHelper.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/references/FirReferenceResolveHelper.kt index 0164b12d778..2f5db53c68d 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/references/FirReferenceResolveHelper.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/references/FirReferenceResolveHelper.kt @@ -140,150 +140,215 @@ internal object FirReferenceResolveHelper { val symbolBuilder = analysisSession.firSymbolBuilder val fir = expression.getOrBuildFir(analysisSession.firResolveState) val session = analysisSession.firResolveState.rootModuleSession - when (fir) { - is FirResolvedTypeRef -> { - if (expression.isPartOfUserTypeRefQualifier()) { - 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)) - } - is FirResolvedQualifier -> { - // TODO refactor that block - val classId = fir.classId ?: return emptyList() - - var parent = expression.parent as? KtDotQualifiedExpression - // Distinguish A.foo() from A(.Companion).foo() - // Make expression.parent as? KtDotQualifiedExpression local function - while (parent != null) { - val selectorExpression = parent.selectorExpression ?: break - if (selectorExpression === expression) { - parent = parent.parent as? KtDotQualifiedExpression - continue - } - val receiverClassId = if (parent.receiverExpression == expression) { - /* - * A.Named.i -> class A - */ - val name = fir.relativeClassFqName?.pathSegments()?.firstOrNull() - name?.let { ClassId(fir.packageFqName, it) } - } else null - val parentFir = selectorExpression.getOrBuildFir(analysisSession.firResolveState) - when { - parentFir is FirQualifiedAccess -> { - return listOfNotNull( - (receiverClassId ?: classId).toTargetPsi(session, symbolBuilder, parentFir.calleeReference) - ) - } - receiverClassId != null -> { - return listOfNotNull(receiverClassId.toTargetPsi(session, symbolBuilder)) - } - else -> parent = parent.parent as? KtDotQualifiedExpression - } - } - return listOfNotNull(classId.toTargetPsi(session, symbolBuilder)) - } - is FirAnnotationCall -> { - val type = fir.typeRef as? FirResolvedTypeRef ?: return emptyList() - return listOfNotNull(type.toTargetSymbol(session, symbolBuilder)) - } - is FirResolvedImport -> { - if (expression.isPartOfQualifiedExpression()) { - return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = false)) - } - - val classId = fir.resolvedClassId - if (classId != null) { - return listOfNotNull(classId.toTargetPsi(session, symbolBuilder)) - } - val name = fir.importedName ?: return emptyList() - val symbolProvider = session.firSymbolProvider - - @OptIn(ExperimentalStdlibApi::class) - return buildList { - symbolProvider.getTopLevelCallableSymbols(fir.packageFqName, name) - .mapTo(this) { it.fir.buildSymbol(symbolBuilder) } - symbolProvider - .getClassLikeSymbolByFqName(ClassId(fir.packageFqName, name)) - ?.fir - ?.buildSymbol(symbolBuilder) - ?.let(::add) - } - } - is FirFile -> { - if (expression.getNonStrictParentOfType() != null) { - // Special: package reference in the middle of package directive - return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = false)) - } - return listOf(symbolBuilder.buildSymbol(fir)) - } + return when (fir) { + is FirResolvedTypeRef -> getSymbolsForResolvedTypeRef(fir, expression, session, symbolBuilder) + is FirResolvedQualifier -> + getSymbolsForResolvedQualifier(fir, expression, session, symbolBuilder, analysisSession) + is FirAnnotationCall -> getSymbolsForAnnotationCall(fir, session, symbolBuilder) + is FirResolvedImport -> getSymbolsByResolvedImport(expression, symbolBuilder, fir, session) + is FirFile -> getSymbolsByFirFile(expression, symbolBuilder, fir) is FirArrayOfCall -> { // We can't yet find PsiElement for arrayOf, intArrayOf, etc. - return emptyList() - } - is FirReturnExpression -> { - return if (expression is KtLabelReferenceExpression) { - listOf(fir.target.labeledElement.buildSymbol(symbolBuilder)) - } else emptyList() - } - is FirErrorNamedReference -> { - val candidates = when (val diagnostic = fir.diagnostic) { - is ConeAmbiguityError -> diagnostic.candidates - is ConeOperatorAmbiguityError -> diagnostic.candidates - is ConeInapplicableCandidateError -> listOf(diagnostic.candidateSymbol) - else -> emptyList() - } - return candidates.mapNotNull { it.fir.buildSymbol(symbolBuilder) } - } - is FirResolvable -> { - val calleeReference = - if (fir is FirFunctionCall - && fir.isImplicitFunctionCall() - && expression is KtNameReferenceExpression - ) { - // we are resolving implicit invoke call, like - // fun foo(a: () -> Unit) { - // a() - // } - (fir.dispatchReceiver as FirQualifiedAccessExpression).calleeReference - } else fir.calleeReference - return listOfNotNull(calleeReference.toTargetSymbol(session, symbolBuilder)) - } - else -> { - // Handle situation when we're in the middle/beginning of qualifier - // A.B.C.foo() or A.B.C.foo() - // NB: in this case we get some parent FIR, like FirBlock, FirProperty, FirFunction or the like - var parent = expression.parent as? KtDotQualifiedExpression - var unresolvedCounter = 1 - while (parent != null) { - val selectorExpression = parent.selectorExpression ?: break - if (selectorExpression === expression) { - parent = parent.parent as? KtDotQualifiedExpression - continue - } - val parentFir = selectorExpression.getOrBuildFir(analysisSession.firResolveState) - if (parentFir is FirResolvedQualifier) { - var classId = parentFir.classId - while (unresolvedCounter > 0) { - unresolvedCounter-- - classId = classId?.outerClassId - } - return listOfNotNull(classId?.toTargetPsi(session, symbolBuilder)) - } - parent = parent.parent as? KtDotQualifiedExpression - unresolvedCounter++ - } - return emptyList() + emptyList() } + is FirReturnExpression -> getSymbolsByReturnExpression(expression, fir, symbolBuilder) + is FirErrorNamedReference -> getSymbolsByErrorNamedReference(fir, symbolBuilder) + is FirResolvable -> getSymbolsByResolvable(fir, expression, session, symbolBuilder) + else -> handleUnknownFirElement(expression, analysisSession, session, symbolBuilder) } } - private fun findPossibleTypeQualifier(qualifier: KtSimpleNameExpression, wholeTypeFir: FirResolvedTypeRef): ClassId? { + private fun handleUnknownFirElement( + expression: KtSimpleNameExpression, + analysisSession: KtFirAnalysisSession, + session: FirSession, + symbolBuilder: KtSymbolByFirBuilder + ): List { + // Handle situation when we're in the middle/beginning of qualifier + // A.B.C.foo() or A.B.C.foo() + // NB: in this case we get some parent FIR, like FirBlock, FirProperty, FirFunction or the like + var parent = expression.parent as? KtDotQualifiedExpression + var unresolvedCounter = 1 + while (parent != null) { + val selectorExpression = parent.selectorExpression ?: break + if (selectorExpression === expression) { + parent = parent.parent as? KtDotQualifiedExpression + continue + } + val parentFir = selectorExpression.getOrBuildFir(analysisSession.firResolveState) + if (parentFir is FirResolvedQualifier) { + var classId = parentFir.classId + while (unresolvedCounter > 0) { + unresolvedCounter-- + classId = classId?.outerClassId + } + return listOfNotNull(classId?.toTargetPsi(session, symbolBuilder)) + } + parent = parent.parent as? KtDotQualifiedExpression + unresolvedCounter++ + } + return emptyList() + } + + private fun getSymbolsByResolvable( + fir: FirResolvable, + expression: KtSimpleNameExpression, + session: FirSession, + symbolBuilder: KtSymbolByFirBuilder + ): List { + val calleeReference = + if (fir is FirFunctionCall + && fir.isImplicitFunctionCall() + && expression is KtNameReferenceExpression + ) { + // we are resolving implicit invoke call, like + // fun foo(a: () -> Unit) { + // a() + // } + (fir.dispatchReceiver as FirQualifiedAccessExpression).calleeReference + } else fir.calleeReference + return listOfNotNull(calleeReference.toTargetSymbol(session, symbolBuilder)) + } + + private fun getSymbolsByErrorNamedReference( + fir: FirErrorNamedReference, + symbolBuilder: KtSymbolByFirBuilder + ): List { + val candidates = when (val diagnostic = fir.diagnostic) { + is ConeAmbiguityError -> diagnostic.candidates + is ConeOperatorAmbiguityError -> diagnostic.candidates + is ConeInapplicableCandidateError -> listOf(diagnostic.candidateSymbol) + else -> emptyList() + } + return candidates.mapNotNull { it.fir.buildSymbol(symbolBuilder) } + } + + private fun getSymbolsByReturnExpression( + expression: KtSimpleNameExpression, + fir: FirReturnExpression, + symbolBuilder: KtSymbolByFirBuilder + ): Collection { + return if (expression is KtLabelReferenceExpression) { + listOf(fir.target.labeledElement.buildSymbol(symbolBuilder)) + } else emptyList() + } + + private fun getSymbolsByFirFile( + expression: KtSimpleNameExpression, + symbolBuilder: KtSymbolByFirBuilder, + fir: FirFile + ): List { + if (expression.getNonStrictParentOfType() != null) { + // Special: package reference in the middle of package directive + return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = false)) + } + return listOf(symbolBuilder.buildSymbol(fir)) + } + + private fun getSymbolsByResolvedImport( + expression: KtSimpleNameExpression, + symbolBuilder: KtSymbolByFirBuilder, + fir: FirResolvedImport, + session: FirSession + ): List { + if (expression.isPartOfQualifiedExpression()) { + return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = false)) + } + + val classId = fir.resolvedClassId + if (classId != null) { + return listOfNotNull(classId.toTargetPsi(session, symbolBuilder)) + } + val name = fir.importedName ?: return emptyList() + val symbolProvider = session.firSymbolProvider + + @OptIn(ExperimentalStdlibApi::class) + return buildList { + symbolProvider.getTopLevelCallableSymbols(fir.packageFqName, name) + .mapTo(this) { it.fir.buildSymbol(symbolBuilder) } + symbolProvider + .getClassLikeSymbolByFqName(ClassId(fir.packageFqName, name)) + ?.fir + ?.buildSymbol(symbolBuilder) + ?.let(::add) + } + } + + private fun getSymbolsForResolvedTypeRef( + fir: FirResolvedTypeRef, + expression: KtSimpleNameExpression, + session: FirSession, + symbolBuilder: KtSymbolByFirBuilder + ): Collection { + if (expression.isPartOfUserTypeRefQualifier()) { + 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)) + } + + private fun getSymbolsForResolvedQualifier( + fir: FirResolvedQualifier, + expression: KtSimpleNameExpression, + session: FirSession, + symbolBuilder: KtSymbolByFirBuilder, + analysisSession: KtFirAnalysisSession + ): Collection { + // TODO refactor that block + val classId = fir.classId ?: return emptyList() + + var parent = expression.parent as? KtDotQualifiedExpression + // Distinguish A.foo() from A(.Companion).foo() + // Make expression.parent as? KtDotQualifiedExpression local function + while (parent != null) { + val selectorExpression = parent.selectorExpression ?: break + if (selectorExpression === expression) { + parent = parent.parent as? KtDotQualifiedExpression + continue + } + val receiverClassId = if (parent.receiverExpression == expression) { + /* + * A.Named.i -> class A + */ + val name = fir.relativeClassFqName?.pathSegments()?.firstOrNull() + name?.let { ClassId(fir.packageFqName, it) } + } else null + val parentFir = selectorExpression.getOrBuildFir(analysisSession.firResolveState) + when { + parentFir is FirQualifiedAccess -> { + return listOfNotNull( + (receiverClassId ?: classId).toTargetPsi(session, symbolBuilder, parentFir.calleeReference) + ) + } + receiverClassId != null -> { + return listOfNotNull(receiverClassId.toTargetPsi(session, symbolBuilder)) + } + else -> parent = parent.parent as? KtDotQualifiedExpression + } + } + return listOfNotNull(classId.toTargetPsi(session, symbolBuilder)) + } + + private fun getSymbolsForAnnotationCall( + fir: FirAnnotationCall, + session: FirSession, + symbolBuilder: KtSymbolByFirBuilder + ): Collection { + val type = fir.typeRef as? FirResolvedTypeRef ?: return emptyList() + return listOfNotNull(type.toTargetSymbol(session, symbolBuilder)) + } + + 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) - val wholeType = (wholeTypeFir.psi as KtTypeReference).typeElement?.unwrapNullable() as? KtUserType ?: return null + val wholeType = + (wholeTypeFir.psi as KtTypeReference).typeElement?.unwrapNullable() as? KtUserType ?: return null val qualifiersToDrop = countQualifiersToDrop(wholeType, qualifierToResolve) return wholeTypeFir.type.classId?.dropLastNestedClasses(qualifiersToDrop) @@ -294,7 +359,8 @@ internal object FirReferenceResolveHelper { * * 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() + 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].