From ff71dda06533b8b5efd3fde630947df486ee3c40 Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Fri, 26 Mar 2021 23:05:05 +0300 Subject: [PATCH] FIR IDE: Add `KtVisibilityChecker` component --- .../idea/frontend/api/KtAnalysisSession.kt | 6 +- .../components/KtVisibilityCheckerMixIn.kt | 30 +++++++ .../frontend/api/fir/KtFirAnalysisSession.kt | 3 + .../fir/components/KtFirVisibilityChecker.kt | 83 +++++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtVisibilityCheckerMixIn.kt create mode 100644 idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirVisibilityChecker.kt diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt index e89d27ebe0a..70020729335 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt @@ -40,7 +40,8 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali KtSymbolsMixIn, KtReferenceResolveMixIn, KtReferenceShortenerMixIn, - KtSymbolDeclarationRendererMixIn { + KtSymbolDeclarationRendererMixIn, + KtVisibilityCheckerMixIn { override val analysisSession: KtAnalysisSession get() = this @@ -90,4 +91,7 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali internal val expressionInfoProvider: KtExpressionInfoProvider get() = expressionInfoProviderImpl protected abstract val expressionInfoProviderImpl: KtExpressionInfoProvider + + internal val visibilityChecker: KtVisibilityChecker get() = visibilityCheckerImpl + protected abstract val visibilityCheckerImpl: KtVisibilityChecker } \ No newline at end of file diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtVisibilityCheckerMixIn.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtVisibilityCheckerMixIn.kt new file mode 100644 index 00000000000..860e41b7b8b --- /dev/null +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtVisibilityCheckerMixIn.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2021 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.idea.frontend.api.components + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFileSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility +import org.jetbrains.kotlin.psi.KtExpression + +abstract class KtVisibilityChecker : KtAnalysisSessionComponent() { + abstract fun isVisible( + candidateSymbol: KtSymbolWithVisibility, + useSiteFile: KtFileSymbol, + position: PsiElement, + receiverExpression: KtExpression? + ): Boolean +} + +interface KtVisibilityCheckerMixIn : KtAnalysisSessionMixIn { + fun isVisible( + candidateSymbol: KtSymbolWithVisibility, + useSiteFile: KtFileSymbol, + receiverExpression: KtExpression? = null, + position: PsiElement + ): Boolean = + analysisSession.visibilityChecker.isVisible(candidateSymbol, useSiteFile, position, receiverExpression) +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt index af3724fdfea..565b8e24354 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/KtFirAnalysisSession.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCo import org.jetbrains.kotlin.idea.fir.low.level.api.api.getFirFile import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRendererProvider import org.jetbrains.kotlin.idea.frontend.api.fir.components.* @@ -61,6 +62,8 @@ private constructor( override val expressionInfoProviderImpl = KtFirExpressionInfoProvider(this, token) + override val visibilityCheckerImpl: KtVisibilityChecker = KtFirVisibilityChecker(this, token) + override val typeProviderImpl = KtFirTypeProvider(this, token) override val typeInfoProviderImpl = KtFirTypeInfoProvider(this, token) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirVisibilityChecker.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirVisibilityChecker.kt new file mode 100644 index 00000000000..7040b034f3d --- /dev/null +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirVisibilityChecker.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2021 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.idea.frontend.api.fir.components + +import com.intellij.psi.PsiElement +import com.intellij.psi.util.parentsOfType +import org.jetbrains.kotlin.fir.FirSymbolOwner +import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration +import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue +import org.jetbrains.kotlin.fir.visibilityChecker +import org.jetbrains.kotlin.idea.fir.low.level.api.api.collectDesignation +import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirSafe +import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker +import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirFileSymbol +import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFileSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility +import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject + +internal class KtFirVisibilityChecker( + override val analysisSession: KtFirAnalysisSession, + override val token: ValidityToken +) : KtVisibilityChecker(), KtFirAnalysisSessionComponent { + + override fun isVisible( + candidateSymbol: KtSymbolWithVisibility, + useSiteFile: KtFileSymbol, + position: PsiElement, + receiverExpression: KtExpression? + ): Boolean { + require(candidateSymbol is KtFirSymbol<*>) + require(useSiteFile is KtFirFileSymbol) + + val nonLocalContainingDeclaration = findContainingNonLocalDeclaration(position) + + return useSiteFile.firRef.withFir { useSiteFirFile -> + val containers = nonLocalContainingDeclaration + ?.getOrBuildFirSafe>(analysisSession.firResolveState) + ?.collectDesignation() + ?.path + .orEmpty() + + val explicitDispatchReceiver = receiverExpression + ?.getOrBuildFirSafe(analysisSession.firResolveState) + ?.let { ExpressionReceiverValue(it) } + + candidateSymbol.firRef.withFir { candidateFirSymbol -> + require(candidateFirSymbol is FirMemberDeclaration && candidateFirSymbol is FirSymbolOwner<*>) { + "$candidateFirSymbol must be a FirMemberDeclaration and FirSymbolOwner; it were ${candidateFirSymbol::class} instead" + } + + rootModuleSession.visibilityChecker.isVisible( + candidateFirSymbol, + rootModuleSession, + useSiteFirFile, + containers, + explicitDispatchReceiver + ) + } + } + } + + private fun findContainingNonLocalDeclaration(element: PsiElement): KtCallableDeclaration? { + return element + .parentsOfType() + .firstOrNull { it.isNotFromLocalClass } + } + + private val KtCallableDeclaration.isNotFromLocalClass + get() = this is KtNamedFunction && (isTopLevel || containingClassOrObject?.isLocal == false) || + this is KtProperty && (isTopLevel || containingClassOrObject?.isLocal == false) +}