[Analysis API] Remove 'FirTowerContextProvider'

The only implementation of 'FirTowerContextProvider' that is not
obsolete is inside the reference shortener.
This commit is contained in:
Yan Zhulanow
2024-01-11 19:09:25 +09:00
committed by Space Team
parent 9227359256
commit 5d7f54500f
3 changed files with 9 additions and 184 deletions
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.AllCandidatesResolver
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.ContextCollector
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
@@ -95,7 +94,7 @@ internal class KtFirReferenceShortener(
kDocQualifiersToShorten = emptyList(),
)
val towerContext = FirTowerContextProviderByContextCollector.create(firResolveSession, declarationToVisit)
val towerContext = FirTowerDataContextProvider.create(firResolveSession, declarationToVisit)
//TODO: collect all usages of available symbols in the file and prevent importing symbols that could introduce name clashes, which
// may alter the meaning of existing code.
@@ -156,12 +155,11 @@ internal class KtFirReferenceShortener(
private fun buildSymbol(firSymbol: FirBasedSymbol<*>): KtSymbol = analysisSession.firSymbolBuilder.buildSymbol(firSymbol)
}
private class FirTowerContextProviderByContextCollector private constructor(
private class FirTowerDataContextProvider private constructor(
private val contextProvider: ContextCollector.ContextProvider
) : FirTowerContextProvider {
) {
companion object {
fun create(firResolveSession: LLFirResolveSession, targetElement: KtElement): FirTowerContextProviderByContextCollector {
fun create(firResolveSession: LLFirResolveSession, targetElement: KtElement): FirTowerDataContextProvider {
val firFile = targetElement.containingKtFile.getOrBuildFirFile(firResolveSession)
val sessionHolder = run {
@@ -181,11 +179,11 @@ private class FirTowerContextProviderByContextCollector private constructor(
filter = { ContextCollector.FilterResponse.CONTINUE }
)
return FirTowerContextProviderByContextCollector(contextProvider)
return FirTowerDataContextProvider(contextProvider)
}
}
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? {
fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? {
for (parent in ktElement.parentsWithSelf) {
val context = contextProvider[parent, ContextCollector.ContextKind.SELF]
@@ -343,7 +341,7 @@ private class FirShorteningContext(val analysisSession: KtFirAnalysisSession) {
fun findScopesAtPosition(
position: KtElement,
newImports: Sequence<FqName>,
towerContextProvider: FirTowerContextProvider,
towerContextProvider: FirTowerDataContextProvider,
withImplicitReceivers: Boolean = true,
): List<FirScope>? {
val towerDataContext = towerContextProvider.getClosestAvailableParentContext(position) ?: return null
@@ -508,7 +506,7 @@ private class CollectingVisitor(private val collector: ElementsToShortenCollecto
private class ElementsToShortenCollector(
private val shortenOptions: ShortenOptions,
private val shorteningContext: FirShorteningContext,
private val towerContextProvider: FirTowerContextProvider,
private val towerContextProvider: FirTowerDataContextProvider,
private val containingFile: KtFile,
private val selection: TextRange,
private val classShortenStrategy: (FirClassLikeSymbol<*>) -> ShortenStrategy,
@@ -978,7 +976,7 @@ private class ElementsToShortenCollector(
val shortClassName = classToImport.shortClassName
if (expression.getReferencedNameAsName() != shortClassName) return
val contextProvider = FirTowerContextProviderByContextCollector.create(firResolveSession, expression)
val contextProvider = FirTowerDataContextProvider.create(firResolveSession, expression)
val positionScopes = shorteningContext.findScopesAtPosition(expression, getNamesToImport(), contextProvider) ?: return
val availableClassifier = shorteningContext.findFirstClassifierInScopesByName(positionScopes, shortClassName) ?: return
when {
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLDiagnosticProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLModuleResolutionStrategyProvider
@@ -1,172 +0,0 @@
/*
* 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.analysis.low.level.api.fir.element.builder
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTowerDataContext
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.addIfNotNull
interface FirTowerContextProvider {
fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext?
}
internal class FileTowerProvider(
private val file: KtFile,
private val context: FirTowerDataContext,
) : FirTowerContextProvider {
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? =
if (file == ktElement.containingKtFile) context else null
}
internal class FirTowerDataContextAllElementsCollector : FirResolveContextCollector, FirTowerContextProvider {
private val elementsToContext: MutableMap<KtElement, FirTowerDataContext> = hashMapOf()
/**
* Class headers have context which is different from the context inside of the class, so we have
* to collect the scopes for it separately.
*
* See [containsInHeader] for more info.
*/
private val classesToClassHeaderContext: MutableMap<KtClassOrObject, FirTowerDataContext> = hashMapOf()
override fun addFileContext(file: FirFile, context: FirTowerDataContext) {
val ktFile = file.psi as? KtFile ?: return
elementsToContext[ktFile] = context
}
override fun addStatementContext(statement: FirStatement, context: BodyResolveContext) {
val closestParentExpression = statement.psi?.closestParentExpressionWithSameContextOrSelf() ?: return
// FIR body transform may alter the context if there are implicit receivers with smartcast
elementsToContext[closestParentExpression] = context.towerDataContext.createSnapshot(keepMutable = false)
}
override fun addDeclarationContext(declaration: FirDeclaration, context: BodyResolveContext) {
val psi = declaration.psi as? KtElement ?: return
// FIR creates a fake field declaration for the delegated super calls,
// and it makes handling class headers more difficult, so
// we do not collect contexts for such declarations
if (psi is KtDelegatedSuperTypeEntry) return
elementsToContext[psi] = context.towerDataContext.createSnapshot(keepMutable = false)
}
override fun addClassHeaderContext(declaration: FirRegularClass, context: FirTowerDataContext) {
val psi = declaration.psi as? KtElement ?: return
if (psi !is KtClassOrObject) return
classesToClassHeaderContext[psi] = context
}
override fun getClosestAvailableParentContext(ktElement: KtElement): FirTowerDataContext? {
var current: PsiElement? = ktElement
while (current != null) {
if (current is KtElement) {
getContextForElementOnPosition(current, ktElement)?.let { return it }
}
if (current is KtFile) {
break
}
current = current.parent
}
return null
}
/**
* N.B. It is expected that [element] contains [position].
*/
private fun getContextForElementOnPosition(element: KtElement, position: KtElement): FirTowerDataContext? {
val contextMapping =
if (element is KtClassOrObject && element.containsInHeader(position)) {
classesToClassHeaderContext
} else {
elementsToContext
}
return getContextForElement(element, contextMapping)
}
private fun getContextForElement(element: KtElement, contextMapping: Map<out KtElement, FirTowerDataContext>): FirTowerDataContext? {
contextMapping[element]?.let { return it }
if (element is KtDeclaration) {
val originalDeclaration = element.originalDeclaration
originalDeclaration?.let { contextMapping[it] }?.let { return it }
}
return null
}
}
/**
* Returns [this] in case [this] is:
* - a statement in a block
* - an initializer of a declaration
* - an expression in when entry
* - a right operand in binary expression with operator `&&` or `||`
*
* Otherwise, invokes this function recursively on the parent.
*/
@Suppress("NO_TAIL_CALLS_FOUND", "NON_TAIL_RECURSIVE_CALL") // K2 warning suppression, TODO: KT-62472
private tailrec fun PsiElement.closestParentExpressionWithSameContextOrSelf(): KtExpression? {
if (this is KtExpression) {
if (
parent is KtBlockExpression ||
parent is KtDeclarationWithInitializer ||
isExpressionInWhenEntry ||
isRightOperandInBinaryLogicOperation
) return this
}
return parent?.closestParentExpressionWithSameContextOrSelf()
}
private val KtExpression.isExpressionInWhenEntry: Boolean
get() = this == (parent as? KtWhenEntry)?.expression
private val KtExpression.isRightOperandInBinaryLogicOperation: Boolean
get() {
val binaryLogicOperation = (parent as? KtBinaryExpression)
?.takeIf { it.operationToken == KtTokens.ANDAND || it.operationToken == KtTokens.OROR }
return this == binaryLogicOperation?.right
}
/**
* Returns true if [element] is considered to be a part of [this] class header.
*
* Here by "header" we understand the parts of the class declaration which resolution is not affected
* by the class own supertypes.
*/
private fun KtClassOrObject.containsInHeader(element: PsiElement): Boolean {
if (this == element) return false
val classHeaderParts = buildSet<KtElement> {
addIfNotNull(getContextReceiverList())
addIfNotNull(modifierList) // for annotations declared on class
addIfNotNull(typeParameterList)
addIfNotNull(typeConstraintList) // `where` clause type constraints
for (superTypeEntry in superTypeListEntries) {
addIfNotNull(superTypeEntry.typeReference)
}
}
if (classHeaderParts.isEmpty()) return false
val elementParentsUntilClass = element.parentsWithSelf.takeWhile { it != this }
return elementParentsUntilClass.any { it in classHeaderParts }
}