org.jetbrains.kotlin.fir.resolve.QualifiedNameResolution.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.getDeprecationForCallSite
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.lookupTracker
import org.jetbrains.kotlin.fir.recordNameLookup
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDeprecated
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeNestedClassAccessedViaInstanceReference
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirTypeCandidateCollector
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
data class QualifierResolutionResult(
val qualifier: FirResolvedQualifier,
val applicability: CandidateApplicability,
)
fun BodyResolveComponents.resolveRootPartOfQualifier(
namedReference: FirSimpleNamedReference,
qualifiedAccess: FirQualifiedAccessExpression,
nonFatalDiagnosticsFromExpression: List?,
): QualifierResolutionResult? {
val name = namedReference.name
if (name.asString() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE) {
return buildResolvedQualifierResult(
qualifiedAccess = qualifiedAccess,
packageFqName = FqName.ROOT,
nonFatalDiagnostics = nonFatalDiagnosticsFromExpression,
)
}
val scopes = createCurrentScopeList()
session.lookupTracker?.recordNameLookup(
name,
scopes.asSequence().flatMap { it.scopeOwnerLookupNames }.asIterable(),
qualifiedAccess.source,
file.source
)
var firstUnsuccessful: FirTypeCandidateCollector.TypeCandidate? = null
for (scope in scopes) {
val candidate = scope.getUnambiguousCandidate(name, this) ?: continue
val symbol = candidate.symbol as? FirClassLikeSymbol ?: continue
// Only return successful candidates here
if (candidate.applicability != CandidateApplicability.RESOLVED) {
if (firstUnsuccessful == null) firstUnsuccessful = candidate
continue
}
return buildResolvedQualifierResultForTopLevelClass(symbol, qualifiedAccess, nonFatalDiagnosticsFromExpression, candidate)
}
// If we have only found one unsuccessful candidate, return it.
if (firstUnsuccessful != null) {
// We checked the type of the symbol in the loop
val symbol = firstUnsuccessful.symbol as FirClassLikeSymbol
return buildResolvedQualifierResultForTopLevelClass(symbol, qualifiedAccess, nonFatalDiagnosticsFromExpression, firstUnsuccessful)
}
return FqName.ROOT.continueQualifierInPackage(
name,
qualifiedAccess,
nonFatalDiagnosticsFromExpression,
this
)
}
fun FirResolvedQualifier.continueQualifier(
namedReference: FirSimpleNamedReference,
qualifiedAccess: FirQualifiedAccessExpression,
nonFatalDiagnosticsFromExpression: List?,
session: FirSession,
components: BodyResolveComponents,
): QualifierResolutionResult? {
val name = namedReference.name
// No symbol means it's a package. Continue resolution in that package.
val outerClassSymbol = symbol ?: return packageFqName.continueQualifierInPackage(
name,
qualifiedAccess,
nonFatalDiagnosticsFromExpression,
components
)
val firClass = outerClassSymbol.fir
if (firClass !is FirClass) return null
val nestedClassifierScope =
firClass.scopeProvider.getNestedClassifierScope(firClass, components.session, components.scopeSession) ?: return null
session.lookupTracker?.recordNameLookup(
name,
nestedClassifierScope.scopeOwnerLookupNames,
qualifiedAccess.source,
components.file.source
)
val candidate = nestedClassifierScope.getUnambiguousCandidate(name, components) ?: return null
val nestedClassSymbol = candidate.symbol as? FirClassLikeSymbol ?: return null
val nonFatalDiagnostics = extractNonFatalDiagnostics(
qualifiedAccess.source,
explicitReceiver = null,
nestedClassSymbol,
extraNotFatalDiagnostics = [email protected],
session
)
return components.buildResolvedQualifierResult(
qualifiedAccess = qualifiedAccess,
packageFqName = [email protected],
relativeClassFqName = [email protected]?.child(name),
symbol = nestedClassSymbol,
nonFatalDiagnostics = nonFatalDiagnostics + nonFatalDiagnosticsFromExpression.orEmpty(),
extraTypeArguments = [email protected],
candidate = candidate
)
}
private fun FirScope.getUnambiguousCandidate(name: Name, components: BodyResolveComponents): FirTypeCandidateCollector.TypeCandidate? {
val collector = FirTypeCandidateCollector(components.session, components.file, components.containingDeclarations)
processClassifiersByName(name, collector::processCandidate)
return collector.getResult().resolvedCandidateOrNull()
}
private fun FqName.continueQualifierInPackage(
name: Name,
qualifiedAccess: FirQualifiedAccessExpression,
nonFatalDiagnosticsFromExpression: List?,
components: BodyResolveComponents,
): QualifierResolutionResult? {
val childFqName = this.child(name)
if (components.symbolProvider.getPackage(childFqName) != null) {
return components.buildResolvedQualifierResult(
qualifiedAccess = qualifiedAccess,
packageFqName = childFqName,
nonFatalDiagnostics = nonFatalDiagnosticsFromExpression,
)
}
val classId = ClassId.topLevel(childFqName)
val symbol = components.symbolProvider.getClassLikeSymbolByClassId(classId) ?: return null
val nonFatalDiagnostics = extractNonFatalDiagnostics(
qualifiedAccess.source,
explicitReceiver = null,
symbol,
extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression,
components.session
)
return components.buildResolvedQualifierResult(
qualifiedAccess = qualifiedAccess,
packageFqName = this@continueQualifierInPackage,
relativeClassFqName = classId.relativeClassName,
symbol = symbol,
nonFatalDiagnostics = nonFatalDiagnostics,
)
}
private fun BodyResolveComponents.buildResolvedQualifierResultForTopLevelClass(
symbol: FirClassLikeSymbol<*>,
qualifiedAccess: FirQualifiedAccessExpression,
nonFatalDiagnosticsFromExpression: List?,
candidate: FirTypeCandidateCollector.TypeCandidate,
): QualifierResolutionResult {
val classId = symbol.classId
val nonFatalDiagnostics = extractNonFatalDiagnostics(
qualifiedAccess.source,
explicitReceiver = null,
symbol,
extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression,
session
)
return buildResolvedQualifierResult(
qualifiedAccess = qualifiedAccess,
packageFqName = classId.packageFqName,
relativeClassFqName = classId.relativeClassName,
symbol = symbol,
nonFatalDiagnostics = nonFatalDiagnostics,
candidate = candidate,
)
}
private fun BodyResolveComponents.buildResolvedQualifierResult(
qualifiedAccess: FirQualifiedAccessExpression,
packageFqName: FqName,
relativeClassFqName: FqName? = null,
symbol: FirClassLikeSymbol<*>? = null,
nonFatalDiagnostics: List? = null,
extraTypeArguments: List? = null,
candidate: FirTypeCandidateCollector.TypeCandidate? = null,
): QualifierResolutionResult {
return QualifierResolutionResult(
buildResolvedQualifierForClass(
symbol = symbol,
sourceElement = qualifiedAccess.source,
packageFqName = packageFqName,
relativeClassName = relativeClassFqName,
typeArgumentsForQualifier = qualifiedAccess.typeArguments.applyIf(!extraTypeArguments.isNullOrEmpty()) { plus(extraTypeArguments.orEmpty()) },
diagnostic = candidate?.diagnostic,
nonFatalDiagnostics = nonFatalDiagnostics.orEmpty(),
annotations = qualifiedAccess.annotations
),
candidate?.applicability ?: CandidateApplicability.RESOLVED,
)
}
internal fun extractNestedClassAccessDiagnostic(
source: KtSourceElement?,
explicitReceiver: FirExpression?,
symbol: FirClassLikeSymbol<*>
): ConeDiagnostic? {
if ((explicitReceiver?.unwrapSmartcastExpression() as? FirResolvedQualifier)?.typeArguments?.isNotEmpty() == true)
return ConeNestedClassAccessedViaInstanceReference(source!!, symbol)
return null
}
internal fun extractNonFatalDiagnostics(
source: KtSourceElement?,
explicitReceiver: FirExpression?,
symbol: FirClassLikeSymbol<*>,
extraNotFatalDiagnostics: List?,
session: FirSession,
): List {
val prevDiagnostics = (explicitReceiver?.unwrapSmartcastExpression() as? FirResolvedQualifier)?.nonFatalDiagnostics ?: emptyList()
var result: MutableList? = null
val deprecation = symbol.getDeprecationForCallSite(session)
if (deprecation != null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
result.add(ConeDeprecated(source, symbol, deprecation))
}
if (extraNotFatalDiagnostics != null && extraNotFatalDiagnostics.isNotEmpty()) {
if (result == null) {
result = mutableListOf()
result.addAll(prevDiagnostics)
}
result.addAll(extraNotFatalDiagnostics)
}
return result?.toList() ?: prevDiagnostics
}