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

org.jetbrains.kotlin.fir.FirEffectiveVisibilityResolverImpl.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2020 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

import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.utils.addToStdlib.safeAs

@ThreadSafeMutableState
open class FirEffectiveVisibilityResolverImpl(private val session: FirSession) : FirEffectiveVisibilityResolver() {
    private val cache = mutableMapOf()

    final override fun resolveFor(
        declaration: FirMemberDeclaration,
        containingDeclarations: List?,
        scopeSession: ScopeSession
    ): FirEffectiveVisibility {
        if (declaration.source != null) {
            cache[declaration.source]?.let {
                return it
            }
        }

        val (parentSymbol, parentEffectiveVisibility) =
            declaration.getParentInfo(containingDeclarations, scopeSession)
        var visibility = declaration.visibility

        // this is a temporary solution until
        // visibility starts to understand
        // overrides itself.
        if (declaration is FirSimpleFunction) {
            parentSymbol?.fir.safeAs>()?.let {
                visibility = declaration.lowestVisibilityAmongOverrides(it, session, scopeSession)
            }
        }

        var selfEffectiveVisibility = computeEffectiveVisibility(visibility, parentSymbol)
        selfEffectiveVisibility = parentEffectiveVisibility.lowerBound(selfEffectiveVisibility)
        return declaration.remember(selfEffectiveVisibility)
    }

    protected open fun computeEffectiveVisibility(visibility: Visibility, containerSymbol: FirClassLikeSymbol<*>?): FirEffectiveVisibility {
        return visibility.normalize().forVisibility(containerSymbol)
    }

    protected fun Visibility.forVisibility(
        containerSymbol: FirClassLikeSymbol<*>?
    ): FirEffectiveVisibility =
        when (this) {
            Visibilities.Private, Visibilities.PrivateToThis, Visibilities.InvisibleFake -> FirEffectiveVisibilityImpl.Private
            Visibilities.Protected -> FirEffectiveVisibilityImpl.Protected(containerSymbol, session)
            Visibilities.Internal -> FirEffectiveVisibilityImpl.Internal
            Visibilities.Public -> FirEffectiveVisibilityImpl.Public
            Visibilities.Local -> FirEffectiveVisibilityImpl.Local
            Visibilities.Unknown -> FirEffectiveVisibilityImpl.Private
            // NB: visibility must be already normalized here, so e.g. no JavaVisibilities are possible at this point
            else -> error("Visibility $name is not allowed in forVisibility")
        }

    private fun FirElement.remember(effectiveVisibility: FirEffectiveVisibility): FirEffectiveVisibility {
        val source = source
        if (source != null) {
            cache[source] = effectiveVisibility
        }
        return effectiveVisibility
    }

    private fun FirMemberDeclaration.getParentInfo(
        containingDeclarations: List?,
        scopeSession: ScopeSession
    ): Pair?, FirEffectiveVisibility> {
        var parentEffectiveVisibility: FirEffectiveVisibility = FirEffectiveVisibilityImpl.Public
        // because for now effective visibility
        // only works with FirClassLikeSymbol's
        var parentSymbol: FirClassLikeSymbol<*>? = null
        val parentClassId = this.getParentClassId()

        // for some reason ClassId for "/"
        // has local = false but still returns
        // null instead of a symbol
        // TODO: fix
        var succeededToGetSymbol = false

        // look for the containing class
        // in the containingDeclarations or
        // try using the firSymbolProvider
        if (parentClassId != null && containingDeclarations != null) {
            parentClassId.findOuterContainerInfo(containingDeclarations, scopeSession)?.let {
                parentSymbol = it.first
                parentEffectiveVisibility = it.second
                succeededToGetSymbol = true
            }
        }

        if (!succeededToGetSymbol) {
            if (parentClassId?.isLocal == false) {
                // ?: is needed to get enum from enum entry
                parentSymbol = session.firSymbolProvider.getClassLikeSymbolByFqName(parentClassId)
                    ?: parentClassId.outerClassId?.let { session.firSymbolProvider.getClassLikeSymbolByFqName(it) }
                parentSymbol?.fir.safeAs()?.let {
                    parentEffectiveVisibility = resolveFor(it, null, scopeSession)
                }
            } else if (parentClassId?.isLocal == true) {
                parentEffectiveVisibility = FirEffectiveVisibilityImpl.Local
            }
        }

        return parentSymbol to parentEffectiveVisibility
    }

    private fun FirDeclaration.getParentClassId(): ClassId? = when (this) {
        is FirCallableMemberDeclaration<*> -> this.symbol.callableId.classId
        is FirClassLikeDeclaration<*> -> this.symbol.classId.outerClassId
        else -> null
    }

    private fun FirDeclaration.getClassId(): ClassId? = when (this) {
        is FirCallableMemberDeclaration<*> -> this.symbol.callableId.classId
        is FirClassLikeDeclaration<*> -> this.symbol.classId
        else -> null
    }

    private fun ClassId.findOuterContainerInfo(
        containingDeclarations: List,
        scopeSession: ScopeSession
    ): Pair, FirEffectiveVisibility>? {
        for (index in containingDeclarations.indices) {
            val declaration = containingDeclarations[index]
            val declarationClassId = declaration.getClassId()

            if (this.relativeClassName == declarationClassId?.relativeClassName) {
                return when (declaration) {
                    is FirRegularClass -> {
                        declaration.symbol to resolveFor(declaration, containingDeclarations.subList(0, index), scopeSession)
                    }
                    is FirAnonymousObject -> {
                        declaration.symbol to declaration.remember(FirEffectiveVisibilityImpl.Local)
                    }
                    else -> {
                        null
                    }
                }
            }
        }

        return null
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy