diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt index 22d1f4f3bdb..95722af82cb 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirReferenceShortener.kt @@ -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, - towerContextProvider: FirTowerContextProvider, + towerContextProvider: FirTowerDataContextProvider, withImplicitReceivers: Boolean = true, ): List? { 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 { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LLFirResolveSession.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LLFirResolveSession.kt index 3d89b915345..d8af27223fd 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LLFirResolveSession.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LLFirResolveSession.kt @@ -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 diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt deleted file mode 100644 index a24f69efec0..00000000000 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirTowerDataContextCollector.kt +++ /dev/null @@ -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 = 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 = 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): 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 { - 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 } -}