KTIJ-26423 [AA] Get rid of FE10 implementation of KtImportOptimizer

As of now, this implementation is not supposed to be used at all

If (for some reason) it becomes relevant again, please refer
to the 7fd441f1 commit which contained a FE10 implementation
of this service (copied from the old FE10 implementation of
import optimizer)
This commit is contained in:
Roman Golyshev
2023-10-24 19:10:12 +02:00
committed by teamcity
parent a6668919b0
commit 4e4f3075bd
2 changed files with 1 additions and 523 deletions
@@ -1,202 +0,0 @@
/*
* Copyright 2010-2022 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.api.descriptors
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
import org.jetbrains.kotlin.psi.psiUtil.isPackageDirectiveExpression
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@Suppress("ClassName")
sealed class CallType<TReceiver : KtElement?>(val descriptorKindFilter: DescriptorKindFilter) {
object UNKNOWN : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DEFAULT : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DOT : CallType<KtExpression>(DescriptorKindFilter.ALL)
object SAFE : CallType<KtExpression>(DescriptorKindFilter.ALL)
object SUPER_MEMBERS : CallType<KtSuperExpression>(
DescriptorKindFilter.CALLABLES exclude DescriptorKindExclude.Extensions exclude AbstractMembersExclude
)
object INFIX : CallType<KtExpression>(DescriptorKindFilter.FUNCTIONS exclude NonInfixExclude)
object OPERATOR : CallType<KtExpression>(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude)
object CALLABLE_REFERENCE : CallType<KtExpression?>(DescriptorKindFilter.CALLABLES exclude CallableReferenceExclude)
object IMPORT_DIRECTIVE : CallType<KtExpression?>(DescriptorKindFilter.ALL)
object PACKAGE_DIRECTIVE : CallType<KtExpression?>(DescriptorKindFilter.PACKAGES)
object TYPE : CallType<KtExpression?>(
DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK)
exclude DescriptorKindExclude.EnumEntry
)
object DELEGATE : CallType<KtExpression?>(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude)
object ANNOTATION : CallType<KtExpression?>(
DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK)
exclude NonAnnotationClassifierExclude
)
private object NonInfixExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor) =
!(descriptor is SimpleFunctionDescriptor && descriptor.isInfix)
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
private object NonOperatorExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor) =
!(descriptor is SimpleFunctionDescriptor && descriptor.isOperator)
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
private object CallableReferenceExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor) /* currently not supported for locals and synthetic */ =
descriptor !is CallableMemberDescriptor || descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
private object NonAnnotationClassifierExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is ClassifierDescriptor) return false
return descriptor !is ClassDescriptor || descriptor.kind != ClassKind.ANNOTATION_CLASS
}
override val fullyExcludedDescriptorKinds: Int get() = 0
}
private object AbstractMembersExclude : DescriptorKindExclude() {
override fun excludes(descriptor: DeclarationDescriptor) =
descriptor is CallableMemberDescriptor && descriptor.modality == Modality.ABSTRACT
override val fullyExcludedDescriptorKinds: Int
get() = 0
}
}
@Suppress("ClassName")
sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallType<TReceiver>>(
val callType: TCallType,
val receiver: TReceiver
) {
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>(CallType.UNKNOWN, null)
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null)
class DOT(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.DOT>(CallType.DOT, receiver)
class SAFE(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.SAFE>(CallType.SAFE, receiver)
class SUPER_MEMBERS(receiver: KtSuperExpression) : CallTypeAndReceiver<KtSuperExpression, CallType.SUPER_MEMBERS>(
CallType.SUPER_MEMBERS, receiver
)
class INFIX(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.INFIX>(CallType.INFIX, receiver)
class OPERATOR(receiver: KtExpression) : CallTypeAndReceiver<KtExpression, CallType.OPERATOR>(CallType.OPERATOR, receiver)
class CALLABLE_REFERENCE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.CALLABLE_REFERENCE>(
CallType.CALLABLE_REFERENCE, receiver
)
class IMPORT_DIRECTIVE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.IMPORT_DIRECTIVE>(
CallType.IMPORT_DIRECTIVE, receiver
)
class PACKAGE_DIRECTIVE(receiver: KtExpression?) :
CallTypeAndReceiver<KtExpression?, CallType.PACKAGE_DIRECTIVE>(CallType.PACKAGE_DIRECTIVE, receiver)
class TYPE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.TYPE>(CallType.TYPE, receiver)
class DELEGATE(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.DELEGATE>(CallType.DELEGATE, receiver)
class ANNOTATION(receiver: KtExpression?) : CallTypeAndReceiver<KtExpression?, CallType.ANNOTATION>(CallType.ANNOTATION, receiver)
companion object {
fun detect(expression: KtSimpleNameExpression): CallTypeAndReceiver<*, *> {
val parent = expression.parent
if (parent is KtCallableReferenceExpression && expression == parent.callableReference) {
return CALLABLE_REFERENCE(parent.receiverExpression)
}
val receiverExpression = expression.getReceiverExpression()
if (expression.isImportDirectiveExpression()) {
return IMPORT_DIRECTIVE(receiverExpression)
}
if (expression.isPackageDirectiveExpression()) {
return PACKAGE_DIRECTIVE(receiverExpression)
}
if (parent is KtUserType) {
val constructorCallee = (parent.parent as? KtTypeReference)?.parent as? KtConstructorCalleeExpression
if (constructorCallee != null && constructorCallee.parent is KtAnnotationEntry) {
return ANNOTATION(receiverExpression)
}
return TYPE(receiverExpression)
}
when (expression) {
is KtOperationReferenceExpression -> {
if (receiverExpression == null) {
return UNKNOWN // incomplete code
}
return when (parent) {
is KtBinaryExpression -> {
if (parent.operationToken == KtTokens.IDENTIFIER)
INFIX(receiverExpression)
else
OPERATOR(receiverExpression)
}
is KtUnaryExpression -> OPERATOR(receiverExpression)
else -> error("Unknown parent for JetOperationReferenceExpression: $parent with text '${parent.text}'")
}
}
is KtNameReferenceExpression -> {
if (receiverExpression == null) {
return DEFAULT
}
if (receiverExpression is KtSuperExpression) {
return SUPER_MEMBERS(receiverExpression)
}
return when (parent) {
is KtCallExpression -> {
if ((parent.parent as KtQualifiedExpression).operationSign == KtTokens.SAFE_ACCESS)
SAFE(receiverExpression)
else
DOT(receiverExpression)
}
is KtQualifiedExpression -> {
if (parent.operationSign == KtTokens.SAFE_ACCESS)
SAFE(receiverExpression)
else
DOT(receiverExpression)
}
else -> error("Unknown parent for JetNameReferenceExpression with receiver: $parent")
}
}
else -> return UNKNOWN
}
}
}
}
@@ -5,42 +5,13 @@
package org.jetbrains.kotlin.analysis.api.descriptors.components
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.components.KtImportOptimizer
import org.jetbrains.kotlin.analysis.api.components.KtImportOptimizerResult
import org.jetbrains.kotlin.analysis.api.descriptors.CallTypeAndReceiver
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.psiBased.base.getResolutionScope
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.references.KDocReference
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.references.fe10.Fe10SyntheticPropertyAccessorReference
import org.jetbrains.kotlin.references.fe10.KtFe10InvokeFunctionReference
import org.jetbrains.kotlin.references.fe10.base.KtFe10Reference
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.*
import org.jetbrains.kotlin.types.error.ErrorFunctionDescriptor
internal class KtFe10ImportOptimizer(
override val analysisSession: KtFe10AnalysisSession
@@ -49,297 +20,6 @@ internal class KtFe10ImportOptimizer(
get() = analysisSession.token
override fun analyseImports(file: KtFile): KtImportOptimizerResult = withValidityAssertion {
val imports = file.importDirectives
val unusedImports = mutableSetOf<KtImportDirective>()
val visitor = CollectUsedDescriptorsVisitor(file)
file.accept(visitor)
val optimizerData = visitor.data
val explicitlyImportedFqNames = mutableSetOf<FqName>()
for (import in imports) {
val fqName = import.importedFqName ?: continue
if (import.alias == null) {
explicitlyImportedFqNames += fqName
}
}
val parentFqNames = mutableSetOf<FqName>()
val importPaths = HashSet<ImportPath>(file.importDirectives.size)
val fqNames = optimizerData.namesToImport
for ((_, fqName) in optimizerData.descriptorsToImport) {
// we don't add parents of explicitly imported fq-names because such imports are not needed
if (fqName in explicitlyImportedFqNames) continue
val parentFqName = fqName.parent()
if (!parentFqName.isRoot) {
parentFqNames.add(parentFqName)
}
}
val invokeFunctionCallFqNames = optimizerData.references.flatMap { reference ->
val element = reference.element
val context = analyze(element) {
(this as KtFe10AnalysisSession).analysisContext.analyze(element, Fe10AnalysisFacade.AnalysisMode.PARTIAL)
}
val mainReference = (element as? KtCallExpression)?.mainReference as? KtFe10InvokeFunctionReference
mainReference?.getTargetDescriptors(context)?.map { it.fqNameSafe }.orEmpty()
}
for (import in imports) {
val importPath = import.importPath ?: continue
val isUsed = when {
importPath.importedName in optimizerData.unresolvedNames -> true
!importPaths.add(importPath) -> false
importPath.isAllUnder -> optimizerData.unresolvedNames.isNotEmpty() || importPath.fqName in parentFqNames
importPath.fqName in fqNames -> importPath.importedName?.let { it in fqNames.getValue(importPath.fqName) } ?: false
importPath.fqName in invokeFunctionCallFqNames -> true
// case for type alias
else -> import.targetDescriptors().firstOrNull()?.let { it.fqNameSafe in fqNames } ?: false
}
if (!isUsed) {
unusedImports += import
}
}
return KtImportOptimizerResult(unusedImports)
}
private data class ImportableDescriptor(
val descriptor: DeclarationDescriptor,
val fqName: FqName,
)
private interface AbstractReference {
val element: KtElement
val dependsOnNames: Collection<Name>
}
private class InputData(
val descriptorsToImport: Set<ImportableDescriptor>,
val namesToImport: Map<FqName, Set<Name>>,
val references: Collection<AbstractReference>,
val unresolvedNames: Set<Name>,
)
private class CollectUsedDescriptorsVisitor(file: KtFile) : KtVisitorVoid() {
private val currentPackageName = file.packageFqName
private val aliases: Map<FqName, List<Name>> = file.importDirectives
.asSequence()
.filter { !it.isAllUnder && it.alias != null }
.mapNotNull { it.importPath }
.groupBy(keySelector = { it.fqName }, valueTransform = { it.importedName as Name })
private val descriptorsToImport = hashSetOf<ImportableDescriptor>()
private val namesToImport = hashMapOf<FqName, MutableSet<Name>>()
private val abstractRefs = ArrayList<AbstractReference>()
private val unresolvedNames = hashSetOf<Name>()
val data get() = InputData(descriptorsToImport, namesToImport, abstractRefs, unresolvedNames)
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitImportList(importList: KtImportList) {
}
override fun visitPackageDirective(directive: KtPackageDirective) {
}
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
if (element is KtLabelReferenceExpression) return
val references = element.references.ifEmpty { return }
val bindingContext = analyze(element) {
(this as KtFe10AnalysisSession).analysisContext.analyze(element, Fe10AnalysisFacade.AnalysisMode.PARTIAL)
}
val isResolved = hasResolvedDescriptor(element, bindingContext)
for (reference in references) {
if (reference !is KtReference) continue
abstractRefs.add(AbstractReferenceImpl(reference))
val names = reference.resolvesByNames.toSet()
if (!isResolved) {
unresolvedNames += names
}
for (target in reference.targets(bindingContext)) {
val importableDescriptor = target.getImportableDescriptor()
val importableFqName = target.importableFqName ?: continue
val parentFqName = importableFqName.parent()
if (target is PackageViewDescriptor && parentFqName == FqName.ROOT) continue // no need to import top-level packages
if (target !is PackageViewDescriptor && parentFqName == currentPackageName && (importableFqName !in aliases)) continue
if (!reference.canBeResolvedViaImport(target, bindingContext)) continue
if (importableDescriptor.name in names && isAccessibleAsMember(importableDescriptor, element, bindingContext)) {
continue
}
val descriptorNames = (aliases[importableFqName].orEmpty() + importableFqName.shortName()).intersect(names)
namesToImport.getOrPut(importableFqName) { hashSetOf() } += descriptorNames
descriptorsToImport += ImportableDescriptor(importableDescriptor, importableFqName)
}
}
}
private fun isAccessibleAsMember(target: DeclarationDescriptor, place: KtElement, bindingContext: BindingContext): Boolean {
if (target.containingDeclaration !is ClassDescriptor) return false
fun isInScope(scope: HierarchicalScope): Boolean {
return when (target) {
is FunctionDescriptor ->
scope.findFunction(target.name, NoLookupLocation.FROM_IDE) { it == target } != null
&& bindingContext[BindingContext.DEPRECATED_SHORT_NAME_ACCESS, place] != true
is PropertyDescriptor ->
scope.findVariable(target.name, NoLookupLocation.FROM_IDE) { it == target } != null
&& bindingContext[BindingContext.DEPRECATED_SHORT_NAME_ACCESS, place] != true
is ClassDescriptor ->
scope.findClassifier(target.name, NoLookupLocation.FROM_IDE) == target
&& bindingContext[BindingContext.DEPRECATED_SHORT_NAME_ACCESS, place] != true
else -> false
}
}
val resolutionScope = place.getResolutionScope(bindingContext) ?: return false
val noImportsScope = resolutionScope.replaceImportingScopes(null)
if (isInScope(noImportsScope)) return true
// classes not accessible through receivers, only their constructors
return if (target is ClassDescriptor) false
else resolutionScope.getImplicitReceiversHierarchy().any { isInScope(it.type.memberScope.memberScopeAsImportingScope()) }
}
private class AbstractReferenceImpl(private val reference: KtReference) : AbstractReference {
override val element: KtElement
get() = reference.element
override val dependsOnNames: Collection<Name>
get() {
val resolvesByNames = reference.resolvesByNames
if (reference is KtInvokeFunctionReference) {
val additionalNames =
(reference.element.calleeExpression as? KtNameReferenceExpression)?.mainReference?.resolvesByNames
if (additionalNames != null) {
return resolvesByNames + additionalNames
}
}
return resolvesByNames
}
override fun toString() = when (reference) {
is Fe10SyntheticPropertyAccessorReference -> {
reference.toString().replace(
"Fe10SyntheticPropertyAccessorReference",
if (reference.getter) "Getter" else "Setter"
)
}
else -> reference.toString().replace("Fe10", "")
}
}
private fun KtReference.targets(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
//class qualifiers that refer to companion objects should be considered (containing) class references
return bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element as? KtReferenceExpression]?.let { listOf(it) }
?: (this as? KtFe10Reference)?.resolveToDescriptors(bindingContext).orEmpty()
}
private fun hasResolvedDescriptor(element: KtElement, bindingContext: BindingContext): Boolean {
return if (element is KtCallElement)
element.getResolvedCall(bindingContext) != null
else
(element.mainReference as? KtFe10Reference)?.resolveToDescriptors(bindingContext)?.let { descriptors ->
descriptors.isNotEmpty() && descriptors.none { it is ErrorFunctionDescriptor }
} == true
}
private val DeclarationDescriptor.importableFqName: FqName?
get() {
if (!canBeReferencedViaImport()) return null
return getImportableDescriptor().fqNameSafe
}
private fun DeclarationDescriptor.canBeReferencedViaImport(): Boolean {
if (this is PackageViewDescriptor ||
DescriptorUtils.isTopLevelDeclaration(this) ||
this is CallableDescriptor && DescriptorUtils.isStaticDeclaration(this)
) {
return !name.isSpecial
}
//Both TypeAliasDescriptor and ClassDescriptor
val parentClassifier = containingDeclaration as? ClassifierDescriptorWithTypeParameters ?: return false
if (!parentClassifier.canBeReferencedViaImport()) return false
return when (this) {
is ConstructorDescriptor -> !parentClassifier.isInner // inner class constructors can't be referenced via import
is ClassDescriptor, is TypeAliasDescriptor -> true
else -> parentClassifier is ClassDescriptor && parentClassifier.kind == ClassKind.OBJECT
}
}
private fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean {
if (this is KDocReference) {
val qualifier = element.getQualifier() ?: return true
return if (target.isExtension) {
val elementHasFunctionDescriptor =
element.resolveMainReferenceToDescriptors(bindingContext).any { it is FunctionDescriptor }
val qualifierHasClassDescriptor =
qualifier.resolveMainReferenceToDescriptors(bindingContext).any { it is ClassDescriptor }
elementHasFunctionDescriptor && qualifierHasClassDescriptor
} else {
false
}
}
return element.canBeResolvedViaImport(target, bindingContext)
}
fun KtElement.resolveMainReferenceToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
return (mainReference as? KtFe10Reference)?.resolveToDescriptors(bindingContext).orEmpty()
}
private fun KtElement.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean {
if (!target.canBeReferencedViaImport()) return false
if (target.isExtension) return true // assume that any type of reference can use imports when resolved to extension
if (this !is KtNameReferenceExpression) return false
val callTypeAndReceiver = CallTypeAndReceiver.detect(this)
if (callTypeAndReceiver.receiver != null) {
if (target !is PropertyDescriptor || !target.type.isExtensionFunctionType) return false
if (callTypeAndReceiver !is CallTypeAndReceiver.DOT && callTypeAndReceiver !is CallTypeAndReceiver.SAFE) return false
val resolvedCall = bindingContext[BindingContext.CALL, this].getResolvedCall(bindingContext)
as? VariableAsFunctionResolvedCall ?: return false
if (resolvedCall.variableCall.explicitReceiverKind.isDispatchReceiver) return false
}
if (parent is KtThisExpression || parent is KtSuperExpression) return false
return true
}
}
private fun KtImportDirective.targetDescriptors(): Collection<DeclarationDescriptor> {
// For codeFragments imports are created in dummy file
//if (this.containingKtFile.doNotAnalyze != null) return emptyList()
val nameExpression = importedReference?.getQualifiedElementSelector() as? KtSimpleNameExpression ?: return emptyList()
val bindingContext = analyze(nameExpression) {
(this as KtFe10AnalysisSession).analysisContext.analyze(nameExpression, Fe10AnalysisFacade.AnalysisMode.FULL)
}
return (nameExpression.mainReference as? KtFe10Reference)?.resolveToDescriptors(bindingContext).orEmpty()
error("FE10 implementation of KtImportOptimizer should not be called from anywhere")
}
}