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 new file mode 100644 index 00000000000..225c4d744c4 --- /dev/null +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/CallType.kt @@ -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(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 46c59888c0a..267606dc70e 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,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() - val importedPackages = HashSet() + val unusedImports = mutableSetOf() + val visitor = CollectUsedDescriptorsVisitor(file) + file.accept(visitor) + val optimizerData = visitor.data - for (import in allUnderImports) { + val explicitlyImportedFqNames = mutableSetOf() + + 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() + 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() + } } \ No newline at end of file diff --git a/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/importOptimizer/Fe10IdeNormalAnalysisSourceModuleAnalysisApiImportOptimizerTestGenerated.java b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/importOptimizer/Fe10IdeNormalAnalysisSourceModuleAnalysisApiImportOptimizerTestGenerated.java new file mode 100644 index 00000000000..3f2f048f7b7 --- /dev/null +++ b/analysis/analysis-api-fe10/tests-gen/org/jetbrains/kotlin/analysis/api/fe10/test/cases/generated/cases/components/importOptimizer/Fe10IdeNormalAnalysisSourceModuleAnalysisApiImportOptimizerTestGenerated.java @@ -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"); + } + } +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/importOptimizer/AbstractAnalysisApiImportOptimizerTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/importOptimizer/AbstractAnalysisApiImportOptimizerTest.kt index 98707e82db2..582934da7ff 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/importOptimizer/AbstractAnalysisApiImportOptimizerTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/components/importOptimizer/AbstractAnalysisApiImportOptimizerTest.kt @@ -14,7 +14,10 @@ import org.jetbrains.kotlin.test.services.assertions abstract class AbstractAnalysisApiImportOptimizerTest : AbstractAnalysisApiBasedSingleModuleTest(){ override fun doTestByFileStructure(ktFiles: List, 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}") } diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt index c2f2ef9ed83..e58e467bfb7 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt @@ -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) }