From 4e4f3075bd7065112b377b2ff239fcc2cacb3eac Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Tue, 24 Oct 2023 19:10:12 +0200 Subject: [PATCH] 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) --- .../analysis/api/descriptors/CallType.kt | 202 ----------- .../components/KtFe10ImportOptimizer.kt | 322 +----------------- 2 files changed, 1 insertion(+), 523 deletions(-) delete mode 100644 analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/CallType.kt diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/CallType.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/CallType.kt deleted file mode 100644 index 225c4d744c4..00000000000 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/CallType.kt +++ /dev/null @@ -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(val descriptorKindFilter: DescriptorKindFilter) { - object UNKNOWN : CallType(DescriptorKindFilter.ALL) - - object DEFAULT : CallType(DescriptorKindFilter.ALL) - - object DOT : CallType(DescriptorKindFilter.ALL) - - object SAFE : CallType(DescriptorKindFilter.ALL) - - object SUPER_MEMBERS : CallType( - DescriptorKindFilter.CALLABLES exclude DescriptorKindExclude.Extensions exclude AbstractMembersExclude - ) - - object INFIX : CallType(DescriptorKindFilter.FUNCTIONS exclude NonInfixExclude) - - object OPERATOR : CallType(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude) - - object CALLABLE_REFERENCE : CallType(DescriptorKindFilter.CALLABLES exclude CallableReferenceExclude) - - object IMPORT_DIRECTIVE : CallType(DescriptorKindFilter.ALL) - - object PACKAGE_DIRECTIVE : CallType(DescriptorKindFilter.PACKAGES) - - object TYPE : CallType( - DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) - exclude DescriptorKindExclude.EnumEntry - ) - - object DELEGATE : CallType(DescriptorKindFilter.FUNCTIONS exclude NonOperatorExclude) - - object ANNOTATION : CallType( - 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>( - val callType: TCallType, - val receiver: TReceiver -) { - object UNKNOWN : CallTypeAndReceiver(CallType.UNKNOWN, null) - object DEFAULT : CallTypeAndReceiver(CallType.DEFAULT, null) - class DOT(receiver: KtExpression) : CallTypeAndReceiver(CallType.DOT, receiver) - class SAFE(receiver: KtExpression) : CallTypeAndReceiver(CallType.SAFE, receiver) - class SUPER_MEMBERS(receiver: KtSuperExpression) : CallTypeAndReceiver( - CallType.SUPER_MEMBERS, receiver - ) - - class INFIX(receiver: KtExpression) : CallTypeAndReceiver(CallType.INFIX, receiver) - class OPERATOR(receiver: KtExpression) : CallTypeAndReceiver(CallType.OPERATOR, receiver) - class CALLABLE_REFERENCE(receiver: KtExpression?) : CallTypeAndReceiver( - CallType.CALLABLE_REFERENCE, receiver - ) - - class IMPORT_DIRECTIVE(receiver: KtExpression?) : CallTypeAndReceiver( - CallType.IMPORT_DIRECTIVE, receiver - ) - - class PACKAGE_DIRECTIVE(receiver: KtExpression?) : - CallTypeAndReceiver(CallType.PACKAGE_DIRECTIVE, receiver) - - class TYPE(receiver: KtExpression?) : CallTypeAndReceiver(CallType.TYPE, receiver) - class DELEGATE(receiver: KtExpression?) : CallTypeAndReceiver(CallType.DELEGATE, receiver) - class ANNOTATION(receiver: KtExpression?) : CallTypeAndReceiver(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 - } - } - } -} diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ImportOptimizer.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ImportOptimizer.kt index 267606dc70e..302d12353ad 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ImportOptimizer.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFe10ImportOptimizer.kt @@ -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() - val visitor = CollectUsedDescriptorsVisitor(file) - file.accept(visitor) - val optimizerData = visitor.data - - val explicitlyImportedFqNames = mutableSetOf() - - for (import in imports) { - val fqName = import.importedFqName ?: continue - if (import.alias == null) { - explicitlyImportedFqNames += fqName - } - } - - val parentFqNames = mutableSetOf() - val importPaths = HashSet(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 - } - - private class InputData( - val descriptorsToImport: Set, - val namesToImport: Map>, - val references: Collection, - val unresolvedNames: Set, - ) - - private class CollectUsedDescriptorsVisitor(file: KtFile) : KtVisitorVoid() { - private val currentPackageName = file.packageFqName - private val aliases: Map> = 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() - private val namesToImport = hashMapOf>() - private val abstractRefs = ArrayList() - private val unresolvedNames = hashSetOf() - - 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 - 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 { - //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 { - 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 { - // 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") } } \ No newline at end of file