mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
KTIJ-26423 [kotlin] Properly resolve conflicting classes in OptimizedImportsBuilder
This is not all the conflicts resolution that is required for 100% correct Import Optimizer implementation, but it suffices most of the time. GitOrigin-RevId: 7732d85966c678efe2ffe7f78328daed50622af9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
54705dd9c0
commit
d2ee644e48
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.k2.codeinsight.imports
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.components.KaScopeContext
|
||||
import org.jetbrains.kotlin.analysis.api.scopes.KaScope
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaClassifierSymbol
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal class HierarchicalScope private constructor(private val scopes: List<KaScope>) {
|
||||
|
||||
/**
|
||||
* Finds classifier symbols corresponding to the given name across the scope layers.
|
||||
*
|
||||
* Symbols from higher priority scope layers are returned first.
|
||||
*
|
||||
* Each list in the sequence represents classifiers found in a single scopes layer.
|
||||
*/
|
||||
fun findClassifiers(name: Name): Sequence<List<KaClassifierSymbol>> {
|
||||
return scopes.asSequence()
|
||||
.map { it.classifiers(name).toList() }
|
||||
.filter { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun KaSession.createFrom(scopeContext: KaScopeContext): HierarchicalScope {
|
||||
val scopeGroupsSorted = scopeContext.scopes
|
||||
.groupBy({ it.kind }, { it.scope })
|
||||
.toSortedMap(compareByDescending { it.indexInTower })
|
||||
|
||||
val scopes = scopeGroupsSorted.values.map { it.asCompositeScope() }
|
||||
|
||||
return HierarchicalScope(scopes)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-2
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import kotlin.collections.filter
|
||||
|
||||
internal fun KaSession.buildOptimizedImports(
|
||||
file: KtFile,
|
||||
@@ -51,8 +52,8 @@ internal class OptimizedImportsBuilder(
|
||||
|
||||
val parentFqName = resultFqName.parent()
|
||||
if (
|
||||
alias == null
|
||||
&& canUseStarImport(importableSymbol, resultFqName) &&
|
||||
alias == null &&
|
||||
canUseStarImport(importableSymbol, resultFqName) &&
|
||||
ImportPath(parentFqName, isAllUnder = true).isAllowedByRules()
|
||||
) {
|
||||
symbolsByParentFqName.getOrPut(parentFqName) { hashSetOf() }.add(importableSymbol)
|
||||
@@ -88,6 +89,30 @@ internal class OptimizedImportsBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
val importingScopeContext = buildScopeContextByImports(file, importsToGenerate.filter { it.isAllUnder })
|
||||
val importingScopes = HierarchicalScope.run { createFrom(importingScopeContext) }
|
||||
|
||||
for (fqName in classNamesToCheck) {
|
||||
val foundClassifiers = importingScopes.findClassifiers(fqName.shortName()).firstOrNull()
|
||||
val singleFoundClassifier = foundClassifiers?.singleOrNull()
|
||||
|
||||
if (singleFoundClassifier?.importableFqName != fqName) {
|
||||
// add explicit import if failed to import with * (or from current package)
|
||||
importsToGenerate.add(ImportPath(fqName, false))
|
||||
|
||||
val parentFqName = fqName.parent()
|
||||
|
||||
val siblingsToImport = symbolsByParentFqName.getValue(parentFqName)
|
||||
for (descriptor in siblingsToImport.filter { it.run {computeImportableName() } == fqName }) {
|
||||
siblingsToImport.remove(descriptor)
|
||||
}
|
||||
|
||||
if (siblingsToImport.isEmpty()) { // star import is not really needed
|
||||
importsToGenerate.remove(ImportPath(parentFqName, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sortedImportsToGenerate = importsToGenerate.sortedWith(KotlinImportPathComparator.create(file))
|
||||
|
||||
val oldImports = file.importDirectives
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.k2.codeinsight.imports
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.components.KaScopeContext
|
||||
import org.jetbrains.kotlin.psi.KtBlockCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
|
||||
internal fun KaSession.buildScopeContextByImports(originalFile: KtFile, importsToGenerate: Collection<ImportPath>): KaScopeContext {
|
||||
val fileWithImports = buildFileWithImports(originalFile, importsToGenerate)
|
||||
|
||||
return fileWithImports.importingScopeContext
|
||||
}
|
||||
|
||||
private fun buildFileWithImports(
|
||||
originalFile: KtFile,
|
||||
importsToGenerate: Collection<ImportPath>
|
||||
): KtFile {
|
||||
val imports = buildString {
|
||||
for (importPath in importsToGenerate) {
|
||||
append("import ")
|
||||
append(importPath)
|
||||
append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this code fragment misses a package declaration
|
||||
val fileWithImports = KtBlockCodeFragment(originalFile.project, "Dummy_" + originalFile.name, "", imports, originalFile)
|
||||
|
||||
return fileWithImports
|
||||
}
|
||||
|
||||
+5
@@ -579,6 +579,11 @@ public abstract class FirJvmOptimizeImportsTestGenerated extends AbstractFirJvmO
|
||||
runTest("../../idea/tests/testData/editor/optimizeImports/common/Enums.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExplicitImportIsNotRemovedToAvoidConflict.kt")
|
||||
public void testExplicitImportIsNotRemovedToAvoidConflict() throws Exception {
|
||||
runTest("../../idea/tests/testData/editor/optimizeImports/common/ExplicitImportIsNotRemovedToAvoidConflict.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunWithThisReference.kt")
|
||||
public void testExtensionFunWithThisReference() throws Exception {
|
||||
runTest("../../idea/tests/testData/editor/optimizeImports/common/ExtensionFunWithThisReference.kt");
|
||||
|
||||
+5
@@ -311,6 +311,11 @@ public abstract class JsOptimizeImportsTestGenerated extends AbstractJsOptimizeI
|
||||
runTest("testData/editor/optimizeImports/common/Enums.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExplicitImportIsNotRemovedToAvoidConflict.kt")
|
||||
public void testExplicitImportIsNotRemovedToAvoidConflict() throws Exception {
|
||||
runTest("testData/editor/optimizeImports/common/ExplicitImportIsNotRemovedToAvoidConflict.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunWithThisReference.kt")
|
||||
public void testExtensionFunWithThisReference() throws Exception {
|
||||
runTest("testData/editor/optimizeImports/common/ExtensionFunWithThisReference.kt");
|
||||
|
||||
+5
@@ -584,6 +584,11 @@ public abstract class JvmOptimizeImportsTestGenerated extends AbstractJvmOptimiz
|
||||
runTest("testData/editor/optimizeImports/common/Enums.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExplicitImportIsNotRemovedToAvoidConflict.kt")
|
||||
public void testExplicitImportIsNotRemovedToAvoidConflict() throws Exception {
|
||||
runTest("testData/editor/optimizeImports/common/ExplicitImportIsNotRemovedToAvoidConflict.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionFunWithThisReference.kt")
|
||||
public void testExtensionFunWithThisReference() throws Exception {
|
||||
runTest("testData/editor/optimizeImports/common/ExtensionFunWithThisReference.kt");
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package dependencyFoo
|
||||
|
||||
class CommonClass
|
||||
|
||||
fun fooOne() {}
|
||||
fun fooTwo() {}
|
||||
fun fooThree() {}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package dependencyBar
|
||||
|
||||
class CommonClass
|
||||
|
||||
fun barOne() {}
|
||||
fun barTwo() {}
|
||||
fun barThree() {}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||
package test
|
||||
|
||||
import dependencyBar.*
|
||||
import dependencyFoo.*
|
||||
import dependencyFoo.CommonClass
|
||||
|
||||
fun barUsage() {
|
||||
barOne()
|
||||
barTwo()
|
||||
barThree()
|
||||
}
|
||||
|
||||
fun fooUsage() {
|
||||
fooOne()
|
||||
fooTwo()
|
||||
fooThree()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
CommonClass()
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||
package test
|
||||
|
||||
import dependencyBar.*
|
||||
import dependencyFoo.*
|
||||
import dependencyFoo.CommonClass
|
||||
|
||||
fun barUsage() {
|
||||
barOne()
|
||||
barTwo()
|
||||
barThree()
|
||||
}
|
||||
|
||||
fun fooUsage() {
|
||||
fooOne()
|
||||
fooTwo()
|
||||
fooThree()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
CommonClass()
|
||||
}
|
||||
Reference in New Issue
Block a user