Analysis API: add normal FE10 import optimizer (taken from plugin code)

This commit is contained in:
Mikhail Glukhikh
2022-05-20 16:24:58 +02:00
committed by Ilya Kirillov
parent c29482f25f
commit 7fd441f16a
5 changed files with 677 additions and 13 deletions
@@ -0,0 +1,202 @@
/*
* 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,15 +5,42 @@
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.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportDirective
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
@@ -22,25 +49,297 @@ internal class KtFe10ImportOptimizer(
get() = analysisSession.token
override fun analyseImports(file: KtFile): KtImportOptimizerResult = withValidityAssertion {
val (allUnderImports, otherImports) = file.importDirectives.partition { it.isAllUnder }
val imports = file.importDirectives
val unusedImports = LinkedHashSet<KtImportDirective>()
val importedPackages = HashSet<FqName>()
val unusedImports = mutableSetOf<KtImportDirective>()
val visitor = CollectUsedDescriptorsVisitor(file)
file.accept(visitor)
val optimizerData = visitor.data
for (import in allUnderImports) {
val explicitlyImportedFqNames = mutableSetOf<FqName>()
for (import in imports) {
val fqName = import.importedFqName ?: continue
if (!importedPackages.add(fqName)) {
unusedImports += import
if (import.alias == null) {
explicitlyImportedFqNames += fqName
}
}
for (import in otherImports) {
val fqName = import.importedFqName ?: continue
if (import.alias == null && fqName.parent() in importedPackages) {
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()
}
}
@@ -0,0 +1,160 @@
/*
* 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.analysis.api.fe10.test.cases.generated.cases.components.importOptimizer;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.fe10.test.configurator.AnalysisApiFe10TestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.importOptimizer.AbstractAnalysisApiImportOptimizerTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/components/importOptimizer/analyseImports")
@TestDataPath("$PROJECT_ROOT")
public class Fe10IdeNormalAnalysisSourceModuleAnalysisApiImportOptimizerTestGenerated extends AbstractAnalysisApiImportOptimizerTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFe10TestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fe10,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Ide
)
);
}
@Test
public void testAllFilesPresentInAnalyseImports() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("unusedAliasedTypeImport.kt")
public void testUnusedAliasedTypeImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/unusedAliasedTypeImport.kt");
}
@Test
@TestMetadata("unusedFunctionImports.kt")
public void testUnusedFunctionImports() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/unusedFunctionImports.kt");
}
@Test
@TestMetadata("unusedInvokeOperatorImport.kt")
public void testUnusedInvokeOperatorImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/unusedInvokeOperatorImport.kt");
}
@Test
@TestMetadata("usedAliasedFunctionReference.kt")
public void testUsedAliasedFunctionReference() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedAliasedFunctionReference.kt");
}
@Test
@TestMetadata("usedAliasedTypeImport.kt")
public void testUsedAliasedTypeImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedAliasedTypeImport.kt");
}
@Test
@TestMetadata("usedFunctionImport.kt")
public void testUsedFunctionImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedFunctionImport.kt");
}
@Test
@TestMetadata("usedInvokeOperatorAliasedImport.kt")
public void testUsedInvokeOperatorAliasedImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedInvokeOperatorAliasedImport.kt");
}
@Test
@TestMetadata("usedInvokeOperatorExplicitImport.kt")
public void testUsedInvokeOperatorExplicitImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedInvokeOperatorExplicitImport.kt");
}
@Test
@TestMetadata("usedInvokeOperatorImport.kt")
public void testUsedInvokeOperatorImport() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedInvokeOperatorImport.kt");
}
@Test
@TestMetadata("usedTypeAsTypeParameter.kt")
public void testUsedTypeAsTypeParameter() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/usedTypeAsTypeParameter.kt");
}
@Nested
@TestMetadata("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors")
@TestDataPath("$PROJECT_ROOT")
public class ReferencesWithErrors {
@Test
public void testAllFilesPresentInReferencesWithErrors() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("usedConstructor_invalidArguments.kt")
public void testUsedConstructor_invalidArguments() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedConstructor_invalidArguments.kt");
}
@Test
@TestMetadata("usedConstructor_missingCall.kt")
public void testUsedConstructor_missingCall() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedConstructor_missingCall.kt");
}
@Test
@TestMetadata("usedExtensionFunction_invalidArguments.kt")
public void testUsedExtensionFunction_invalidArguments() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedExtensionFunction_invalidArguments.kt");
}
@Test
@TestMetadata("usedExtensionProperty_invalidReceiver.kt")
public void testUsedExtensionProperty_invalidReceiver() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedExtensionProperty_invalidReceiver.kt");
}
@Test
@TestMetadata("usedInvokeOperator_invalidArguments.kt")
public void testUsedInvokeOperator_invalidArguments() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedInvokeOperator_invalidArguments.kt");
}
@Test
@TestMetadata("usedTypeAsTypeParameter_missingOuterType.kt")
public void testUsedTypeAsTypeParameter_missingOuterType() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedTypeAsTypeParameter_missingOuterType.kt");
}
@Test
@TestMetadata("usedTypeImport_missingGeneric.kt")
public void testUsedTypeImport_missingGeneric() throws Exception {
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/usedTypeImport_missingGeneric.kt");
}
}
}
@@ -14,7 +14,10 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractAnalysisApiImportOptimizerTest : AbstractAnalysisApiBasedSingleModuleTest(){
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
val mainKtFile = ktFiles.singleOrNull() ?: ktFiles.first { it.name == "main.kt" }
val unusedImports = analyseForTest(mainKtFile) { analyseImports(mainKtFile).unusedImports }
val unusedImports = analyseForTest(mainKtFile) {
val results = analyseImports(mainKtFile)
results.unusedImports
}
val unusedImportPaths = unusedImports
.map { it.importPath ?: error("Import $it should have an import path, instead was ${it.text}") }
@@ -280,7 +280,7 @@ private fun AnalysisApiTestGroup.generateAnalysisApiComponentsTests() {
component("importOptimizer") {
test(
AbstractAnalysisApiImportOptimizerTest::class,
filter = frontendIs(FrontendKind.Fir) and analysisSessionModeIs(AnalysisSessionMode.Normal),
filter = analysisSessionModeIs(AnalysisSessionMode.Normal),
) {
model("analyseImports", pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME)
}