All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.fir.resolve.QualifiedNameResolution.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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.fullyExpandedClass
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.builder.buildResolvedQualifier
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.calls.getSingleVisibleClassifier
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDeprecated
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeNestedClassAccessedViaInstanceReference
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.visibilityChecker
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

fun BodyResolveComponents.resolveRootPartOfQualifier(
    namedReference: FirSimpleNamedReference,
    qualifiedAccess: FirQualifiedAccessExpression,
    nonFatalDiagnosticsFromExpression: List?,
): FirResolvedQualifier? {
    val name = namedReference.name
    if (name.asString() == ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE) {
        return buildResolvedQualifier {
            this.source = qualifiedAccess.source
            packageFqName = FqName.ROOT
            this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
            annotations += qualifiedAccess.annotations
        }.apply {
            setTypeOfQualifier(this@resolveRootPartOfQualifier)
        }
    }

    val scopes = createCurrentScopeList()
    session.lookupTracker?.recordNameLookup(
        name,
        scopes.asSequence().flatMap { it.scopeOwnerLookupNames }.asIterable(),
        qualifiedAccess.source,
        file.source
    )
    for (scope in scopes) {
        scope.getSingleVisibleClassifier(session, this, name)?.let {
            val klass = (it as? FirClassLikeSymbol<*>)?.fullyExpandedClass(session)
                ?: return@let

            val isVisible = session.visibilityChecker.isClassLikeVisible(
                klass.fir,
                session,
                file,
                containingDeclarations,
            )
            if (!isVisible) {
                return@let
            }
            val classId = it.classId
            return buildResolvedQualifier {
                this.source = qualifiedAccess.source
                packageFqName = classId.packageFqName
                relativeClassFqName = classId.relativeClassName
                symbol = it
                this.typeArguments.addAll(qualifiedAccess.typeArguments)
                this.nonFatalDiagnostics.addAll(
                    extractNonFatalDiagnostics(
                        qualifiedAccess.source,
                        explicitReceiver = null,
                        it,
                        extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression,
                        session
                    )
                )
                annotations += qualifiedAccess.annotations
            }.apply {
                setTypeOfQualifier(this@resolveRootPartOfQualifier)
            }
        }
    }

    return FqName.ROOT.continueQualifierInPackage(
        name,
        qualifiedAccess,
        nonFatalDiagnosticsFromExpression,
        this
    )
}

fun FirResolvedQualifier.continueQualifier(
    namedReference: FirSimpleNamedReference,
    qualifiedAccess: FirQualifiedAccessExpression,
    nonFatalDiagnosticsFromExpression: List?,
    session: FirSession,
    components: BodyResolveComponents,
): FirResolvedQualifier? {
    val name = namedReference.name
    symbol?.let { outerClassSymbol ->
        val firClass = outerClassSymbol.fir
        if (firClass !is FirClass) return null
        return firClass.scopeProvider.getNestedClassifierScope(firClass, components.session, components.scopeSession)
            ?.also {
                session.lookupTracker?.recordNameLookup(name, it.scopeOwnerLookupNames, qualifiedAccess.source, components.file.source)
            }
            ?.getSingleVisibleClassifier(session, components, name)
            ?.takeIf { it is FirClassLikeSymbol<*> }
            ?.let { nestedClassSymbol ->
                buildResolvedQualifier {
                    this.source = qualifiedAccess.source
                    packageFqName = [email protected]
                    relativeClassFqName = [email protected]?.child(name)
                    symbol = nestedClassSymbol as FirClassLikeSymbol<*>
                    isFullyQualified = true

                    this.typeArguments.clear()
                    this.typeArguments.addAll(qualifiedAccess.typeArguments)
                    this.typeArguments.addAll([email protected])
                    this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
                    this.nonFatalDiagnostics.addAll(
                        extractNonFatalDiagnostics(
                            qualifiedAccess.source,
                            explicitReceiver = null,
                            nestedClassSymbol,
                            extraNotFatalDiagnostics = [email protected],
                            session
                        )
                    )
                }.apply {
                    setTypeOfQualifier(components)
                }
            }
    }

    return packageFqName.continueQualifierInPackage(
        name,
        qualifiedAccess,
        nonFatalDiagnosticsFromExpression,
        components
    )
}

private fun FqName.continueQualifierInPackage(
    name: Name,
    qualifiedAccess: FirQualifiedAccessExpression,
    nonFatalDiagnosticsFromExpression: List?,
    components: BodyResolveComponents
): FirResolvedQualifier? {
    val childFqName = this.child(name)
    if (components.symbolProvider.getPackage(childFqName) != null) {
        return buildResolvedQualifier {
            this.source = qualifiedAccess.source
            packageFqName = childFqName
            this.typeArguments.addAll(qualifiedAccess.typeArguments)
            this.nonFatalDiagnostics.addAll(nonFatalDiagnosticsFromExpression.orEmpty())
            annotations += qualifiedAccess.annotations
        }.apply {
            setTypeOfQualifier(components)
        }
    }

    val classId = ClassId.topLevel(childFqName)
    val symbol = components.symbolProvider.getClassLikeSymbolByClassId(classId) ?: return null

    return buildResolvedQualifier {
        this.source = qualifiedAccess.source
        packageFqName = this@continueQualifierInPackage
        relativeClassFqName = classId.relativeClassName
        this.symbol = symbol
        this.typeArguments.addAll(qualifiedAccess.typeArguments)
        this.nonFatalDiagnostics.addAll(
            extractNonFatalDiagnostics(
                qualifiedAccess.source,
                explicitReceiver = null,
                symbol,
                extraNotFatalDiagnostics = nonFatalDiagnosticsFromExpression,
                components.session
            )
        )
        isFullyQualified = true
        annotations += qualifiedAccess.annotations
    }.apply {
        setTypeOfQualifier(components)
    }
}

internal fun extractNestedClassAccessDiagnostic(
    source: KtSourceElement?,
    explicitReceiver: FirExpression?,
    symbol: FirClassLikeSymbol<*>
): ConeDiagnostic? {
    if ((explicitReceiver 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 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
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy