diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/OptimizedImportsBuilder.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/OptimizedImportsBuilder.kt index 42ef85434f3..8f80c9bc5ae 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/OptimizedImportsBuilder.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/OptimizedImportsBuilder.kt @@ -60,7 +60,8 @@ class OptimizedImportsBuilder( } data class InputData( - val descriptorsToImport: Map>, + val descriptorsToImport: Set, + val namesToImport: Map>, val references: Collection ) @@ -119,9 +120,9 @@ class OptimizedImportsBuilder( .mapTo(importsToGenerate) { it.importPath } val descriptorsByParentFqName = HashMap>() - for ((descriptor, names) in data.descriptorsToImport) { - for (name in names) { - val fqName = descriptor.importableFqName!! + for (descriptor in data.descriptorsToImport) { + val fqName = descriptor.importableFqName!! + for (name in data.namesToImport.getValue(fqName)) { val alias = if (name != fqName.shortName()) name else null val explicitImportPath = ImportPath(fqName, false, alias) @@ -149,16 +150,16 @@ class OptimizedImportsBuilder( || !starImportPath.isAllowedByRules() if (useExplicitImports) { fqNames - .filter { !isImportedByDefault(it) } + .filter(this::needExplicitImport) .mapTo(importsToGenerate) { ImportPath(it, false) } } else { descriptors .asSequence() .filterIsInstance() .map { it.importableFqName!! } - .filterTo(classNamesToCheck) { !isImportedByDefault(it) } + .filterTo(classNamesToCheck, this::needExplicitImport) - if (!fqNames.all(this::isImportedByDefault)) { + if (fqNames.all(this::needExplicitImport)) { importsToGenerate.add(starImportPath) } } @@ -205,7 +206,7 @@ class OptimizedImportsBuilder( (oldTargets + newTargets).forEach { lockImportForDescriptor( it, - data.descriptorsToImport.getOrElse(it) { listOf(it.name) }.intersect(names) + data.namesToImport.getOrElse(it.importableFqName!!) { listOf(it.name) }.intersect(names) ) } } @@ -321,6 +322,12 @@ class OptimizedImportsBuilder( } } + private fun needExplicitImport(fqName: FqName): Boolean = hasAlias(fqName) || !isImportedByDefault(fqName) + + private fun hasAlias(fqName: FqName) = data.namesToImport[fqName]?.let { + it.singleOrNull() == null + } ?: false + private fun isImportedByDefault(fqName: FqName) = importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file) private fun isImportedByLowPriorityDefault(fqName: FqName) = diff --git a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt index 25bd3306744..0811bebb276 100644 --- a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt @@ -79,11 +79,12 @@ class KotlinImportOptimizer : ImportOptimizer { .mapNotNull { it.importPath } .groupBy(keySelector = { it.fqName }, valueTransform = { it.importedName as Name }) - private val descriptorsToImport = LinkedHashMap>() + private val descriptorsToImport = LinkedHashSet() + private val namesToImport = LinkedHashMap>() private val abstractRefs = ArrayList() val data: OptimizedImportsBuilder.InputData - get() = OptimizedImportsBuilder.InputData(descriptorsToImport, abstractRefs) + get() = OptimizedImportsBuilder.InputData(descriptorsToImport, namesToImport, abstractRefs) override fun visitElement(element: PsiElement) { ProgressIndicatorProvider.checkCanceled() @@ -119,7 +120,8 @@ class KotlinImportOptimizer : ImportOptimizer { if (isAccessibleAsMember(importableDescriptor, element, bindingContext)) continue val descriptorNames = (aliases[importableFqName].orEmpty() + importableFqName.shortName()).intersect(names) - descriptorsToImport.getOrPut(importableDescriptor) { LinkedHashSet() } += descriptorNames + namesToImport.getOrPut(importableFqName) { LinkedHashSet() } += descriptorNames + descriptorsToImport += importableDescriptor } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinUnusedImportInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinUnusedImportInspection.kt index 62217fd054a..a11dbdea05e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinUnusedImportInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinUnusedImportInspection.kt @@ -49,7 +49,6 @@ import org.jetbrains.kotlin.idea.imports.OptimizedImportsBuilder import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtImportDirective @@ -75,12 +74,10 @@ class KotlinUnusedImportInspection : AbstractKotlinInspection() { .map { it.fqName } .toSet() - val fqNames = HashMap>() + val fqNames = optimizerData.namesToImport val parentFqNames = HashSet() - for ((descriptor, names) in optimizerData.descriptorsToImport) { + for (descriptor in optimizerData.descriptorsToImport) { val fqName = descriptor.importableFqName!! - fqNames.compute(fqName) { _, u -> u?.plus(names) ?: names } - if (fqName !in explicitlyImportedFqNames) { // we don't add parents of explicitly imported fq-names because such imports are not needed val parentFqName = fqName.parent() if (!parentFqName.isRoot) { diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.dependency.kt b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.dependency.kt new file mode 100644 index 00000000000..65ede26550f --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.dependency.kt @@ -0,0 +1,9 @@ +package pack1 + +fun a() { + +} + +fun Int.a() { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt new file mode 100644 index 00000000000..628ff0c021f --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt @@ -0,0 +1,15 @@ +import pack1.a +import pack1.a as a1 +import kotlin.collections.List +import kotlin.collections.List as List1 +import kotlin.run as run1 +import kotlin.run + +fun foo() { + run {} + run1 {} + List(1) {} + List1(1) {} + a() + a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.after b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.after new file mode 100644 index 00000000000..7d2af8c3a01 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.after @@ -0,0 +1,15 @@ +import pack1.a +import kotlin.collections.List +import kotlin.run +import pack1.a as a1 +import kotlin.collections.List as List1 +import kotlin.run as run1 + +fun foo() { + run {} + run1 {} + List(1) {} + List1(1) {} + a() + a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.log b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.log new file mode 100644 index 00000000000..6e42be03e4a --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt.log @@ -0,0 +1,37 @@ +Additional checking of reference Getter: List +Additional checking of reference KtSimpleNameReference: List +Additional checking of reference Getter: List1 +Additional checking of reference KtSimpleNameReference: List1 +Changed resolve of KtSimpleNameReference: List1 +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference Getter: run +Additional checking of reference KtSimpleNameReference: run +Additional checking of reference Getter: run1 +Additional checking of reference KtSimpleNameReference: run1 +Changed resolve of KtSimpleNameReference: run1 +Additional checking of reference KtInvokeFunctionReference: List1(1) {} +Additional checking of reference KtInvokeFunctionReference: List(1) {} +Additional checking of reference KtInvokeFunctionReference: a1() +Additional checking of reference KtInvokeFunctionReference: run {} +Additional checking of reference KtInvokeFunctionReference: run1 {} +Trying to build import list again with import rules: +pack1.a as a1, +kotlin.collections.List as List1, +kotlin.run as run1 +Additional checking of reference Getter: List +Additional checking of reference KtSimpleNameReference: List +Additional checking of reference Getter: List1 +Additional checking of reference KtSimpleNameReference: List1 +Changed resolve of KtSimpleNameReference: List1 +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference Getter: run +Additional checking of reference KtSimpleNameReference: run +Additional checking of reference Getter: run1 +Additional checking of reference KtSimpleNameReference: run1 +Changed resolve of KtSimpleNameReference: run1 +Additional checking of reference KtInvokeFunctionReference: List1(1) {} +Additional checking of reference KtInvokeFunctionReference: List(1) {} +Additional checking of reference KtInvokeFunctionReference: a1() +Additional checking of reference KtInvokeFunctionReference: run {} +Additional checking of reference KtInvokeFunctionReference: run1 {} diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.dependency.kt b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.dependency.kt new file mode 100644 index 00000000000..65ede26550f --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.dependency.kt @@ -0,0 +1,9 @@ +package pack1 + +fun a() { + +} + +fun Int.a() { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt new file mode 100644 index 00000000000..7f643ba7119 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt @@ -0,0 +1,10 @@ +import pack1.a +import pack1.a as a1 +import kotlin.run as run1 +import kotlin.run + +fun foo() { + run1 {} + a() + 42.a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.after b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.after new file mode 100644 index 00000000000..a8c85438605 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.after @@ -0,0 +1,9 @@ +import pack1.a +import pack1.a as a1 +import kotlin.run as run1 + +fun foo() { + run1 {} + a() + 42.a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.log b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.log new file mode 100644 index 00000000000..5f4a52d6f33 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt.log @@ -0,0 +1,17 @@ +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference Getter: run1 +Additional checking of reference KtSimpleNameReference: run1 +Changed resolve of KtSimpleNameReference: run1 +Additional checking of reference KtInvokeFunctionReference: a1() +Additional checking of reference KtInvokeFunctionReference: run1 {} +Trying to build import list again with import rules: +pack1.a as a1, +kotlin.run as run1 +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference Getter: run1 +Additional checking of reference KtSimpleNameReference: run1 +Changed resolve of KtSimpleNameReference: run1 +Additional checking of reference KtInvokeFunctionReference: a1() +Additional checking of reference KtInvokeFunctionReference: run1 {} diff --git a/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.after b/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.after index 55be2b246f2..7c87269930e 100644 --- a/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.after +++ b/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.after @@ -1,5 +1,5 @@ // NAME_COUNT_TO_USE_STAR_IMPORT: 2 -import p1.A +import p1.* import p2.A fun f() { diff --git a/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.log b/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.log index 6a2daa08bc9..22546bfe039 100644 --- a/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.log +++ b/idea/testData/editor/optimizeImports/common/TwoConstructors.kt.log @@ -6,3 +6,4 @@ Additional checking of reference KtSimpleNameReference: A Changed resolve of KtSimpleNameReference: A Additional checking of reference KtInvokeFunctionReference: A("") Additional checking of reference KtInvokeFunctionReference: A(1) +Trying to build import list again with import rules: +p2.A, +p1.* diff --git a/idea/testData/editor/optimizeImports/common/WithAlias.dependency.kt b/idea/testData/editor/optimizeImports/common/WithAlias.dependency.kt new file mode 100644 index 00000000000..65ede26550f --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias.dependency.kt @@ -0,0 +1,9 @@ +package pack1 + +fun a() { + +} + +fun Int.a() { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/WithAlias.kt b/idea/testData/editor/optimizeImports/common/WithAlias.kt new file mode 100644 index 00000000000..2c867bfb121 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias.kt @@ -0,0 +1,6 @@ +import pack1.a +import pack1.a as a1 + +fun foo() { + 42.a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/WithAlias.kt.after b/idea/testData/editor/optimizeImports/common/WithAlias.kt.after new file mode 100644 index 00000000000..27f74f28e49 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias.kt.after @@ -0,0 +1,5 @@ +import pack1.a as a1 + +fun foo() { + 42.a1() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/WithAlias.kt.log b/idea/testData/editor/optimizeImports/common/WithAlias.kt.log new file mode 100644 index 00000000000..98f60490c23 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias.kt.log @@ -0,0 +1,9 @@ +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference KtInvokeFunctionReference: a1() +Trying to build import list again with import rules: +pack1.a as a1 +Additional checking of reference Getter: a1 +Additional checking of reference KtSimpleNameReference: a1 +Changed resolve of KtSimpleNameReference: a1 +Additional checking of reference KtInvokeFunctionReference: a1() diff --git a/idea/testData/editor/optimizeImports/common/WithAlias2.dependency.kt b/idea/testData/editor/optimizeImports/common/WithAlias2.dependency.kt new file mode 100644 index 00000000000..65ede26550f --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias2.dependency.kt @@ -0,0 +1,9 @@ +package pack1 + +fun a() { + +} + +fun Int.a() { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/WithAlias2.kt b/idea/testData/editor/optimizeImports/common/WithAlias2.kt new file mode 100644 index 00000000000..110226de1cc --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias2.kt @@ -0,0 +1,6 @@ +import pack1.a +import pack1.a as a1 + +fun foo() { + a() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/WithAlias2.kt.after b/idea/testData/editor/optimizeImports/common/WithAlias2.kt.after new file mode 100644 index 00000000000..21a8c719484 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/WithAlias2.kt.after @@ -0,0 +1,5 @@ +import pack1.a + +fun foo() { + a() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/jvm/AlreadyOptimized.kt.log b/idea/testData/editor/optimizeImports/jvm/AlreadyOptimized.kt.log index 0f7bd155da0..55507368bda 100644 --- a/idea/testData/editor/optimizeImports/jvm/AlreadyOptimized.kt.log +++ b/idea/testData/editor/optimizeImports/jvm/AlreadyOptimized.kt.log @@ -2,3 +2,8 @@ Additional checking of reference Getter: JavaFile Additional checking of reference KtSimpleNameReference: JavaFile Changed resolve of KtSimpleNameReference: JavaFile Additional checking of reference KtInvokeFunctionReference: JavaFile(StringBuilder().append("Hello").toString()) +Trying to build import list again with import rules: +java.io.File as JavaFile +Additional checking of reference Getter: JavaFile +Additional checking of reference KtSimpleNameReference: JavaFile +Changed resolve of KtSimpleNameReference: JavaFile +Additional checking of reference KtInvokeFunctionReference: JavaFile(StringBuilder().append("Hello").toString()) diff --git a/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java index ece7a4e46e5..d7a3ec26dc5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java @@ -79,6 +79,16 @@ public class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeImportsTes runTest("idea/testData/editor/optimizeImports/common/CurrentPackage.kt"); } + @TestMetadata("DefaultImportAndAlias.kt") + public void testDefaultImportAndAlias() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt"); + } + + @TestMetadata("DefaultImportAndAlias2.kt") + public void testDefaultImportAndAlias2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt"); + } + @TestMetadata("DefaultObjectReference.kt") public void testDefaultObjectReference() throws Exception { runTest("idea/testData/editor/optimizeImports/common/DefaultObjectReference.kt"); @@ -179,6 +189,16 @@ public class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeImportsTes runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport.kt"); } + @TestMetadata("WithAlias.kt") + public void testWithAlias() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/WithAlias.kt"); + } + + @TestMetadata("WithAlias2.kt") + public void testWithAlias2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/WithAlias2.kt"); + } + @TestMetadata("idea/testData/editor/optimizeImports/common/kt21515") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java index 505d0233044..2a832462eb3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java @@ -272,6 +272,16 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/common/CurrentPackage.kt"); } + @TestMetadata("DefaultImportAndAlias.kt") + public void testDefaultImportAndAlias() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/DefaultImportAndAlias.kt"); + } + + @TestMetadata("DefaultImportAndAlias2.kt") + public void testDefaultImportAndAlias2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/DefaultImportAndAlias2.kt"); + } + @TestMetadata("DefaultObjectReference.kt") public void testDefaultObjectReference() throws Exception { runTest("idea/testData/editor/optimizeImports/common/DefaultObjectReference.kt"); @@ -372,6 +382,16 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport.kt"); } + @TestMetadata("WithAlias.kt") + public void testWithAlias() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/WithAlias.kt"); + } + + @TestMetadata("WithAlias2.kt") + public void testWithAlias2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/WithAlias2.kt"); + } + @TestMetadata("idea/testData/editor/optimizeImports/common/kt21515") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)