From 4de582564cdb92eb253d80a905e3dc7deb7f431d Mon Sep 17 00:00:00 2001 From: Dmitry Gridin Date: Wed, 17 Jun 2020 16:10:21 +0700 Subject: [PATCH] ImportOptimizer: basic support for removing unresolved imports #KT-32409 Fixed Part of #KT-31331 --- .../idea/imports/OptimizedImportsBuilder.kt | 15 ++++++----- .../idea/imports/KotlinImportOptimizer.kt | 11 +++++--- .../common/ConflictWithUnresolvedName.kt | 9 +++++++ .../ConflictWithUnresolvedName.kt.after | 7 ++++++ .../common/ConflictWithUnresolvedName2.kt | 9 +++++++ .../ConflictWithUnresolvedName2.kt.after | 9 +++++++ .../editor/optimizeImports/common/Kt32409.kt | 7 ++++++ .../optimizeImports/common/Kt32409.kt.after | 7 ++++++ .../common/UnresolvedImport.kt.after | 1 - .../common/UnresolvedImport2.dependency.kt | 4 +++ .../common/UnresolvedImport2.kt | 9 +++++++ .../common/UnresolvedImport2.kt.after | 6 +++++ .../jvm/allUnderImports/NameCountSetting.kt | 2 +- .../allUnderImports/NameCountSetting.kt.after | 2 +- ...ttingWithUnresolvedReference.dependency.kt | 4 +++ ...NameCountSettingWithUnresolvedReference.kt | 14 +++++++++++ ...untSettingWithUnresolvedReference.kt.after | 12 +++++++++ .../optimizeImports/destructuringAtTop.kt | 4 ++- .../destructuringAtTop.kt.after | 4 ++- .../JsOptimizeImportsTestGenerated.java | 20 +++++++++++++++ .../JvmOptimizeImportsTestGenerated.java | 25 +++++++++++++++++++ 21 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt create mode 100644 idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt.after create mode 100644 idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt create mode 100644 idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt.after create mode 100644 idea/testData/editor/optimizeImports/common/Kt32409.kt create mode 100644 idea/testData/editor/optimizeImports/common/Kt32409.kt.after create mode 100644 idea/testData/editor/optimizeImports/common/UnresolvedImport2.dependency.kt create mode 100644 idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt create mode 100644 idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt.after create mode 100644 idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.dependency.kt create mode 100644 idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt create mode 100644 idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt.after diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/imports/OptimizedImportsBuilder.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/imports/OptimizedImportsBuilder.kt index f883b6f30f5..19e1f270c84 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/imports/OptimizedImportsBuilder.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/imports/OptimizedImportsBuilder.kt @@ -53,7 +53,8 @@ class OptimizedImportsBuilder( class InputData( val descriptorsToImport: Set, val namesToImport: Map>, - val references: Collection + val references: Collection, + val unresolvedNames: Set, ) class Options( @@ -81,9 +82,8 @@ class OptimizedImportsBuilder( fun buildOptimizedImports(): List? { val facade = file.getResolutionFacade() file.importDirectives - .filterNot { it.canResolve(facade) } - .mapNotNull(KtImportDirective::getImportPath) - .mapTo(importRules) { ImportRule.Add(it) } + .filter { it.isAllUnder && data.unresolvedNames.isNotEmpty() || it.importedName in data.unresolvedNames && !it.canResolve(facade) } + .mapNotNullTo(importRules) { it.importPath?.let { path -> ImportRule.Add(path) } } while (true) { ProgressManager.checkCanceled() @@ -106,7 +106,7 @@ class OptimizedImportsBuilder( } private fun tryBuildOptimizedImports(): List? { - val importsToGenerate = HashSet() + val importsToGenerate = hashSetOf() importRules.filterIsInstance().mapTo(importsToGenerate) { it.importPath } val descriptorsByParentFqName = HashMap>() @@ -120,15 +120,14 @@ class OptimizedImportsBuilder( val parentFqName = fqName.parent() if (alias == null && canUseStarImport(descriptor, fqName) && ImportPath(parentFqName, true).isAllowedByRules()) { - descriptorsByParentFqName.getOrPut(parentFqName) { LinkedHashSet() }.add(descriptor) + descriptorsByParentFqName.getOrPut(parentFqName) { hashSetOf() }.add(descriptor) } else { importsToGenerate.add(explicitImportPath) } } } - val classNamesToCheck = HashSet() - + val classNamesToCheck = hashSetOf() for (parentFqName in descriptorsByParentFqName.keys) { ProgressManager.checkCanceled() diff --git a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt index 3ff3ff8f061..5f1d35bfddb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/imports/KotlinImportOptimizer.kt @@ -120,12 +120,14 @@ class KotlinImportOptimizer : ImportOptimizer { private val descriptorsToImport = hashSetOf() private val namesToImport = hashMapOf>() private val abstractRefs = ArrayList() + private val unresolvedNames = hashSetOf() val data: OptimizedImportsBuilder.InputData get() = OptimizedImportsBuilder.InputData( descriptorsToImport, namesToImport, abstractRefs, + unresolvedNames, ) override fun visitElement(element: PsiElement) { @@ -148,12 +150,13 @@ class KotlinImportOptimizer : ImportOptimizer { abstractRefs.add(AbstractReferenceImpl(reference)) val names = reference.resolvesByNames + var isResolved = false val bindingContext = element.analyze(BodyResolveMode.PARTIAL) - val targets = reference.targets(bindingContext) - for (target in targets) { - val importableDescriptor = target.getImportableDescriptor() + for (target in reference.targets(bindingContext)) { + isResolved = true + 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 @@ -168,6 +171,8 @@ class KotlinImportOptimizer : ImportOptimizer { namesToImport.getOrPut(importableFqName) { hashSetOf() } += descriptorNames descriptorsToImport += importableDescriptor } + + if (!isResolved && reference is KtSimpleNameReference) unresolvedNames += names } super.visitKtElement(element) diff --git a/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt new file mode 100644 index 00000000000..14c79380eaf --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt @@ -0,0 +1,9 @@ +package srt + +import srt.A + +const val A = 4 + +fun test(a: A) { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt.after b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt.after new file mode 100644 index 00000000000..37d58ec1c4e --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt.after @@ -0,0 +1,7 @@ +package srt + +const val A = 4 + +fun test(a: A) { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt new file mode 100644 index 00000000000..0bf19e9e4c9 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt @@ -0,0 +1,9 @@ +package srt + +import srt.* + +const val A = 4 + +fun test(b: A) { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt.after b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt.after new file mode 100644 index 00000000000..0bf19e9e4c9 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt.after @@ -0,0 +1,9 @@ +package srt + +import srt.* + +const val A = 4 + +fun test(b: A) { + +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/Kt32409.kt b/idea/testData/editor/optimizeImports/common/Kt32409.kt new file mode 100644 index 00000000000..d849fe3dda6 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/Kt32409.kt @@ -0,0 +1,7 @@ +package a.b + +import a.* + +class Foo( + val bar: Bar +) \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/Kt32409.kt.after b/idea/testData/editor/optimizeImports/common/Kt32409.kt.after new file mode 100644 index 00000000000..d849fe3dda6 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/Kt32409.kt.after @@ -0,0 +1,7 @@ +package a.b + +import a.* + +class Foo( + val bar: Bar +) \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/UnresolvedImport.kt.after b/idea/testData/editor/optimizeImports/common/UnresolvedImport.kt.after index 1a4564fdd44..687b05d8d87 100644 --- a/idea/testData/editor/optimizeImports/common/UnresolvedImport.kt.after +++ b/idea/testData/editor/optimizeImports/common/UnresolvedImport.kt.after @@ -1,7 +1,6 @@ import pack1.Class4 import pack1.Example import pack2.* -import pack3.Ggd fun method(a: Class4) { Example() diff --git a/idea/testData/editor/optimizeImports/common/UnresolvedImport2.dependency.kt b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.dependency.kt new file mode 100644 index 00000000000..4c6cbe2fbf2 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.dependency.kt @@ -0,0 +1,4 @@ +package pack1 + +class Example +class Class4 \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt new file mode 100644 index 00000000000..d2934845f5e --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt @@ -0,0 +1,9 @@ +import pack1.Class4 +import pack1.Example +import pack2.* +import pack3.Ggd +import pack4.Gga as Arrr + +fun method(a: Class4) { + Example() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt.after b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt.after new file mode 100644 index 00000000000..caf9d4407b8 --- /dev/null +++ b/idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt.after @@ -0,0 +1,6 @@ +import pack1.Class4 +import pack1.Example + +fun method(a: Class4) { + Example() +} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt index 259f611685d..bf3100bbd32 100644 --- a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt +++ b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt @@ -7,7 +7,7 @@ import java.io.File import java.io.PrintStream import dependency.* -fun foo(c: C) { +fun foo(c: Int) { val v = HashMap>() "".extFun() "".extFun(1) diff --git a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt.after b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt.after index 84c48331b68..2624c4a664e 100644 --- a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt.after +++ b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt.after @@ -5,7 +5,7 @@ import dependency.extFun import java.io.File import java.util.* -fun foo(c: C) { +fun foo(c: Int) { val v = HashMap>() "".extFun() "".extFun(1) diff --git a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.dependency.kt b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.dependency.kt new file mode 100644 index 00000000000..88eba818d5b --- /dev/null +++ b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.dependency.kt @@ -0,0 +1,4 @@ +package dependency + +fun String.extFun() {} +fun String.extFun(p: Int) {} \ No newline at end of file diff --git a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt new file mode 100644 index 00000000000..259f611685d --- /dev/null +++ b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt @@ -0,0 +1,14 @@ +// NAME_COUNT_TO_USE_STAR_IMPORT: 2 +package ppp + +import java.util.HashMap +import java.util.ArrayList +import java.io.File +import java.io.PrintStream +import dependency.* + +fun foo(c: C) { + val v = HashMap>() + "".extFun() + "".extFun(1) +} diff --git a/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt.after b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt.after new file mode 100644 index 00000000000..6cc49f69eec --- /dev/null +++ b/idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt.after @@ -0,0 +1,12 @@ +// NAME_COUNT_TO_USE_STAR_IMPORT: 2 +package ppp + +import dependency.* +import java.io.File +import java.util.* + +fun foo(c: C) { + val v = HashMap>() + "".extFun() + "".extFun(1) +} diff --git a/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt b/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt index 71034b4db9b..55f867593eb 100644 --- a/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt +++ b/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt @@ -3,4 +3,6 @@ import kotlin.collections.* -val (a, b, c) = Triple(1, 2, 3) \ No newline at end of file +fun test() { + val (a, b, c) = Triple(1, 2, 3) +} diff --git a/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt.after b/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt.after index 47d8f1033eb..7b10b64c1f5 100644 --- a/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt.after +++ b/idea/testData/quickfix/optimizeImports/destructuringAtTop.kt.after @@ -1,4 +1,6 @@ // "Optimize imports" "true" // WITH_RUNTIME -val (a, b, c) = Triple(1, 2, 3) \ No newline at end of file +fun test() { + val (a, b, c) = Triple(1, 2, 3) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java index 471590236fc..415484a69cc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/imports/JsOptimizeImportsTestGenerated.java @@ -73,6 +73,16 @@ public class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeImportsTes runTest("idea/testData/editor/optimizeImports/common/ConflictWithAlias2.kt"); } + @TestMetadata("ConflictWithUnresolvedName.kt") + public void testConflictWithUnresolvedName() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt"); + } + + @TestMetadata("ConflictWithUnresolvedName2.kt") + public void testConflictWithUnresolvedName2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt"); + } + @TestMetadata("CurrentPackage.kt") public void testCurrentPackage() throws Exception { runTest("idea/testData/editor/optimizeImports/common/CurrentPackage.kt"); @@ -158,6 +168,11 @@ public class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeImportsTes runTest("idea/testData/editor/optimizeImports/common/Kt2709.kt"); } + @TestMetadata("Kt32409.kt") + public void testKt32409() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/Kt32409.kt"); + } + @TestMetadata("MemberImports.kt") public void testMemberImports() throws Exception { runTest("idea/testData/editor/optimizeImports/common/MemberImports.kt"); @@ -198,6 +213,11 @@ public class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeImportsTes runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport.kt"); } + @TestMetadata("UnresolvedImport2.kt") + public void testUnresolvedImport2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt"); + } + @TestMetadata("WithAlias.kt") public void testWithAlias() throws Exception { runTest("idea/testData/editor/optimizeImports/common/WithAlias.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java index 11847f11ebd..35e1ceff041 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/imports/JvmOptimizeImportsTestGenerated.java @@ -217,6 +217,11 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSetting.kt"); } + @TestMetadata("NameCountSettingWithUnresolvedReference.kt") + public void testNameCountSettingWithUnresolvedReference() throws Exception { + runTest("idea/testData/editor/optimizeImports/jvm/allUnderImports/NameCountSettingWithUnresolvedReference.kt"); + } + @TestMetadata("PackagesToUseStarImport.kt") public void testPackagesToUseStarImport() throws Exception { runTest("idea/testData/editor/optimizeImports/jvm/allUnderImports/PackagesToUseStarImport.kt"); @@ -266,6 +271,16 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/common/ConflictWithAlias2.kt"); } + @TestMetadata("ConflictWithUnresolvedName.kt") + public void testConflictWithUnresolvedName() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName.kt"); + } + + @TestMetadata("ConflictWithUnresolvedName2.kt") + public void testConflictWithUnresolvedName2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/ConflictWithUnresolvedName2.kt"); + } + @TestMetadata("CurrentPackage.kt") public void testCurrentPackage() throws Exception { runTest("idea/testData/editor/optimizeImports/common/CurrentPackage.kt"); @@ -351,6 +366,11 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/common/Kt2709.kt"); } + @TestMetadata("Kt32409.kt") + public void testKt32409() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/Kt32409.kt"); + } + @TestMetadata("MemberImports.kt") public void testMemberImports() throws Exception { runTest("idea/testData/editor/optimizeImports/common/MemberImports.kt"); @@ -391,6 +411,11 @@ public class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimizeImportsT runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport.kt"); } + @TestMetadata("UnresolvedImport2.kt") + public void testUnresolvedImport2() throws Exception { + runTest("idea/testData/editor/optimizeImports/common/UnresolvedImport2.kt"); + } + @TestMetadata("WithAlias.kt") public void testWithAlias() throws Exception { runTest("idea/testData/editor/optimizeImports/common/WithAlias.kt");