Optimize imports to prevent accedential changes in resolve because of import priorities
#KT-11640 Fixed
This commit is contained in:
@@ -19,23 +19,14 @@
|
|||||||
package org.jetbrains.kotlin.idea.imports
|
package org.jetbrains.kotlin.idea.imports
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
|
||||||
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
|
||||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||||
import org.jetbrains.kotlin.renderer.render
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -97,117 +88,3 @@ fun KtReferenceExpression.getImportableTargets(bindingContext: BindingContext):
|
|||||||
return targets.map { it.getImportableDescriptor() }.toSet()
|
return targets.map { it.getImportableDescriptor() }.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prepareOptimizedImports(
|
|
||||||
file: KtFile,
|
|
||||||
descriptorsToImport: Collection<DeclarationDescriptor>,
|
|
||||||
nameCountToUseStarImport: Int,
|
|
||||||
nameCountToUseStarImportForMembers: Int,
|
|
||||||
isInPackagesToUseStarImport: (FqName) -> Boolean
|
|
||||||
): List<ImportPath>? {
|
|
||||||
val importInsertHelper = ImportInsertHelper.getInstance(file.project)
|
|
||||||
val aliasImports = buildAliasImportMap(file)
|
|
||||||
|
|
||||||
val importsToGenerate = HashSet<ImportPath>()
|
|
||||||
|
|
||||||
val descriptorsByParentFqName = hashMapOf<FqName, MutableSet<DeclarationDescriptor>>()
|
|
||||||
for (descriptor in descriptorsToImport) {
|
|
||||||
val fqName = descriptor.importableFqName!!
|
|
||||||
val container = descriptor.containingDeclaration
|
|
||||||
val parentFqName = fqName.parent()
|
|
||||||
val canUseStarImport = when {
|
|
||||||
parentFqName.isRoot -> false
|
|
||||||
(container as? ClassDescriptor)?.kind == ClassKind.OBJECT -> false
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
if (canUseStarImport) {
|
|
||||||
val descriptors = descriptorsByParentFqName.getOrPut(parentFqName) { hashSetOf() }
|
|
||||||
descriptors.add(descriptor)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
importsToGenerate.add(ImportPath(fqName, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val classNamesToCheck = HashSet<FqName>()
|
|
||||||
|
|
||||||
fun isImportedByDefault(fqName: FqName) = importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file)
|
|
||||||
|
|
||||||
for (parentFqName in descriptorsByParentFqName.keys) {
|
|
||||||
val descriptors = descriptorsByParentFqName[parentFqName]!!
|
|
||||||
val fqNames = descriptors.map { it.importableFqName!! }.toSet()
|
|
||||||
val isMember = descriptors.first().containingDeclaration is ClassDescriptor
|
|
||||||
val nameCountToUseStar = if (isMember)
|
|
||||||
nameCountToUseStarImportForMembers
|
|
||||||
else
|
|
||||||
nameCountToUseStarImport
|
|
||||||
val explicitImports = fqNames.size < nameCountToUseStar && !isInPackagesToUseStarImport(parentFqName)
|
|
||||||
if (explicitImports) {
|
|
||||||
for (fqName in fqNames) {
|
|
||||||
if (!isImportedByDefault(fqName)) {
|
|
||||||
importsToGenerate.add(ImportPath(fqName, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (descriptor in descriptors) {
|
|
||||||
if (descriptor is ClassDescriptor) {
|
|
||||||
classNamesToCheck.add(descriptor.importableFqName!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fqNames.all(::isImportedByDefault)) {
|
|
||||||
importsToGenerate.add(ImportPath(parentFqName, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check that there are no conflicts and all classes are really imported
|
|
||||||
val fileWithImportsText = buildString {
|
|
||||||
append("package ").append(file.packageFqName.toUnsafe().render()).append("\n")
|
|
||||||
importsToGenerate.filter { it.isAllUnder }.map { "import " + it.pathStr }.joinTo(this, "\n")
|
|
||||||
}
|
|
||||||
val fileWithImports = KtPsiFactory(file).createAnalyzableFile("Dummy.kt", fileWithImportsText, file)
|
|
||||||
val scope = fileWithImports.getResolutionFacade().getFileResolutionScope(fileWithImports)
|
|
||||||
|
|
||||||
for (fqName in classNamesToCheck) {
|
|
||||||
if (scope.findClassifier(fqName.shortName(), NoLookupLocation.FROM_IDE)?.importableFqName != fqName) {
|
|
||||||
// add explicit import if failed to import with * (or from current package)
|
|
||||||
importsToGenerate.add(ImportPath(fqName, false))
|
|
||||||
|
|
||||||
val parentFqName = fqName.parent()
|
|
||||||
|
|
||||||
val parentDescriptors = descriptorsByParentFqName[parentFqName]!!
|
|
||||||
for (descriptor in parentDescriptors.filter { it.importableFqName == fqName }) {
|
|
||||||
parentDescriptors.remove(descriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentDescriptors.isEmpty()) { // star import is not really needed
|
|
||||||
importsToGenerate.remove(ImportPath(parentFqName, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: drop unused aliases?
|
|
||||||
aliasImports.mapTo(importsToGenerate) { ImportPath(it.value, false, it.key) }
|
|
||||||
|
|
||||||
val sortedImportsToGenerate = importsToGenerate.sortedWith(importInsertHelper.importSortComparator)
|
|
||||||
|
|
||||||
// check if no changes to imports required
|
|
||||||
val oldImports = file.importDirectives
|
|
||||||
if (oldImports.size == sortedImportsToGenerate.size && oldImports.map { it.importPath } == sortedImportsToGenerate) return null
|
|
||||||
|
|
||||||
return sortedImportsToGenerate
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildAliasImportMap(file: KtFile): Map<Name, FqName> {
|
|
||||||
val imports = file.importDirectives
|
|
||||||
val aliasImports = HashMap<Name, FqName>()
|
|
||||||
for (import in imports) {
|
|
||||||
val path = import.importPath ?: continue
|
|
||||||
val aliasName = path.alias
|
|
||||||
if (aliasName != null && aliasName != path.fqnPart().shortName() /* we do not keep trivial aliases */) {
|
|
||||||
aliasImports.put(aliasName, path.fqnPart())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aliasImports
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.imports
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.renderer.render
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||||
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class OptimizedImportsBuilder(
|
||||||
|
private val file: KtFile,
|
||||||
|
private val data: InputData,
|
||||||
|
private val options: Options
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@TestOnly
|
||||||
|
var testLog: StringBuilder? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbstractReference {
|
||||||
|
val element: KtElement
|
||||||
|
val dependsOnNames: Collection<Name>
|
||||||
|
fun resolve(bindingContext: BindingContext): Collection<DeclarationDescriptor>
|
||||||
|
}
|
||||||
|
|
||||||
|
data class InputData(
|
||||||
|
val descriptorsToImport: Set<DeclarationDescriptor>,
|
||||||
|
val references: Collection<AbstractReference>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Options(
|
||||||
|
val nameCountToUseStarImport: Int,
|
||||||
|
val nameCountToUseStarImportForMembers: Int,
|
||||||
|
val isInPackagesToUseStarImport: (FqName) -> Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
private val importInsertHelper = ImportInsertHelper.getInstance(file.project)
|
||||||
|
|
||||||
|
private val lockedImports = HashSet<ImportPath>()
|
||||||
|
|
||||||
|
fun buildOptimizedImports(): List<ImportPath>? {
|
||||||
|
// TODO: should we drop unused aliases?
|
||||||
|
// keep all non-trivial aliases
|
||||||
|
file.importDirectives
|
||||||
|
.mapNotNull { it.importPath }
|
||||||
|
.filterTo(lockedImports) {
|
||||||
|
val aliasName = it.alias
|
||||||
|
aliasName != null && aliasName != it.fqnPart().shortName()
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val lockedImportsBefore = lockedImports.size
|
||||||
|
val result = tryBuildOptimizedImports()
|
||||||
|
if (lockedImports.size == lockedImportsBefore) return result
|
||||||
|
testLog?.append("Trying to build import list again with locked imports: ${lockedImports.joinToString { it.pathStr }}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExpressionToAnalyze(element: KtElement): KtExpression? {
|
||||||
|
val parent = element.parent
|
||||||
|
return when {
|
||||||
|
parent is KtQualifiedExpression && element == parent.selectorExpression -> parent
|
||||||
|
parent is KtCallExpression && element == parent.calleeExpression -> getExpressionToAnalyze(parent)
|
||||||
|
parent is KtUserType -> null //TODO: is it always correct?
|
||||||
|
else -> element as? KtExpression //TODO: what if not expression? Example: KtPropertyDelegationMethodsReference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryBuildOptimizedImports(): List<ImportPath>? {
|
||||||
|
val importsToGenerate = HashSet<ImportPath>()
|
||||||
|
importsToGenerate.addAll(lockedImports)
|
||||||
|
|
||||||
|
val descriptorsByParentFqName = HashMap<FqName, MutableSet<DeclarationDescriptor>>()
|
||||||
|
for (descriptor in data.descriptorsToImport) {
|
||||||
|
val fqName = descriptor.importableFqName!!
|
||||||
|
|
||||||
|
val explicitImportPath = ImportPath(fqName, false)
|
||||||
|
if (explicitImportPath in lockedImports) continue
|
||||||
|
|
||||||
|
if (canUseStarImport(descriptor, fqName)) {
|
||||||
|
descriptorsByParentFqName.getOrPut(fqName.parent()) { HashSet() }.add(descriptor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
importsToGenerate.add(explicitImportPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val classNamesToCheck = HashSet<FqName>()
|
||||||
|
|
||||||
|
for (parentFqName in descriptorsByParentFqName.keys) {
|
||||||
|
val starImportPath = ImportPath(parentFqName, true)
|
||||||
|
if (starImportPath in lockedImports) continue
|
||||||
|
|
||||||
|
val descriptors = descriptorsByParentFqName[parentFqName]!!
|
||||||
|
val fqNames = descriptors.map { it.importableFqName!! }.toSet()
|
||||||
|
val nameCountToUseStar = descriptors.first().nameCountToUseStar()
|
||||||
|
val useExplicitImports = fqNames.size < nameCountToUseStar && !options.isInPackagesToUseStarImport(parentFqName)
|
||||||
|
if (useExplicitImports) {
|
||||||
|
fqNames
|
||||||
|
.filter { !isImportedByDefault(it) }
|
||||||
|
.mapTo(importsToGenerate) { ImportPath(it, false) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
descriptors
|
||||||
|
.filterIsInstance<ClassDescriptor>()
|
||||||
|
.mapTo(classNamesToCheck) { it.importableFqName!! }
|
||||||
|
|
||||||
|
if (!fqNames.all(this::isImportedByDefault)) {
|
||||||
|
importsToGenerate.add(starImportPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check that there are no conflicts and all classes are really imported
|
||||||
|
addExplicitImportsForClassesWhenRequired(classNamesToCheck, descriptorsByParentFqName, importsToGenerate, file)
|
||||||
|
|
||||||
|
val sortedImportsToGenerate = importsToGenerate.sortedWith(importInsertHelper.importSortComparator)
|
||||||
|
|
||||||
|
// check if no changes to imports required
|
||||||
|
val oldImports = file.importDirectives
|
||||||
|
if (oldImports.size == sortedImportsToGenerate.size && oldImports.map { it.importPath } == sortedImportsToGenerate) return null
|
||||||
|
|
||||||
|
val originalFileScope = file.getFileResolutionScope()
|
||||||
|
val newFileScope = buildScopeByImports(file, sortedImportsToGenerate)
|
||||||
|
|
||||||
|
var references = data.references
|
||||||
|
if (testLog != null) {
|
||||||
|
// to make log the same for all runs
|
||||||
|
references = references.sortedBy { it.toString() }
|
||||||
|
}
|
||||||
|
for ((names, refs) in references.groupBy { it.dependsOnNames }) {
|
||||||
|
if (!areScopeSlicesEqual(originalFileScope, newFileScope, names)) {
|
||||||
|
for (ref in refs) {
|
||||||
|
val element = ref.element
|
||||||
|
val bindingContext = element.analyze()
|
||||||
|
val expressionToAnalyze = getExpressionToAnalyze(element) ?: continue
|
||||||
|
val newScope = element.getResolutionScope(bindingContext, file.getResolutionFacade()).replaceImportingScopes(newFileScope)
|
||||||
|
val newBindingContext = expressionToAnalyze.analyzeInContext(newScope, expressionToAnalyze)
|
||||||
|
|
||||||
|
testLog?.append("Additional checking of reference $ref\n")
|
||||||
|
|
||||||
|
val oldTargets = ref.resolve(bindingContext)
|
||||||
|
val newTargets = ref.resolve(newBindingContext)
|
||||||
|
if (!areTargetsEqual(oldTargets, newTargets)) {
|
||||||
|
testLog?.append("Changed resolve of $ref\n")
|
||||||
|
(oldTargets + newTargets).forEach { lockImportForDescriptor(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedImportsToGenerate
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lockImportForDescriptor(descriptor: DeclarationDescriptor) {
|
||||||
|
val fqName = descriptor.importableFqName ?: return
|
||||||
|
val explicitImportPath = ImportPath(fqName, false)
|
||||||
|
val starImportPath = ImportPath(fqName.parent(), true)
|
||||||
|
if (file.importDirectives.any { it.importPath == explicitImportPath }) {
|
||||||
|
lockedImports.add(explicitImportPath)
|
||||||
|
}
|
||||||
|
else if (file.importDirectives.any { it.importPath == starImportPath }) {
|
||||||
|
lockedImports.add(starImportPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addExplicitImportsForClassesWhenRequired(
|
||||||
|
classNamesToCheck: Collection<FqName>,
|
||||||
|
descriptorsByParentFqName: Map<FqName, MutableSet<DeclarationDescriptor>>,
|
||||||
|
importsToGenerate: MutableSet<ImportPath>,
|
||||||
|
originalFile: KtFile
|
||||||
|
) {
|
||||||
|
val scope = buildScopeByImports(originalFile, importsToGenerate.filter { it.isAllUnder })
|
||||||
|
for (fqName in classNamesToCheck) {
|
||||||
|
if (scope.findClassifier(fqName.shortName(), NoLookupLocation.FROM_IDE)?.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 = descriptorsByParentFqName[parentFqName]!!
|
||||||
|
for (descriptor in siblingsToImport.filter { it.importableFqName == fqName }) {
|
||||||
|
siblingsToImport.remove(descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (siblingsToImport.isEmpty()) { // star import is not really needed
|
||||||
|
importsToGenerate.remove(ImportPath(parentFqName, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildScopeByImports(originalFile: KtFile, importsToGenerate: Collection<ImportPath>): ImportingScope {
|
||||||
|
val fileText = buildString {
|
||||||
|
append("package ")
|
||||||
|
append(originalFile.packageFqName.toUnsafe().render())
|
||||||
|
append("\n")
|
||||||
|
|
||||||
|
for (importPath in importsToGenerate) {
|
||||||
|
append("import ")
|
||||||
|
append(importPath.pathStr)
|
||||||
|
if (importPath.hasAlias()) {
|
||||||
|
append("=")
|
||||||
|
append(importPath.alias!!.render())
|
||||||
|
}
|
||||||
|
append("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val fileWithImports = KtPsiFactory(originalFile).createAnalyzableFile("Dummy.kt", fileText, originalFile)
|
||||||
|
return fileWithImports.getFileResolutionScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtFile.getFileResolutionScope() = getResolutionFacade().frontendService<FileScopeProvider>().getFileScopes(this).importingScope
|
||||||
|
|
||||||
|
private fun areScopeSlicesEqual(scope1: ImportingScope, scope2: ImportingScope, names: Collection<Name>): Boolean {
|
||||||
|
val tower1 = scope1.extractSliceTower(names)
|
||||||
|
val tower2 = scope2.extractSliceTower(names)
|
||||||
|
val iterator1 = tower1.iterator()
|
||||||
|
val iterator2 = tower2.iterator()
|
||||||
|
while (true) {
|
||||||
|
if (!iterator1.hasNext()) {
|
||||||
|
return !iterator2.hasNext()
|
||||||
|
}
|
||||||
|
else if (!iterator2.hasNext()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!areTargetsEqual(iterator1.next(), iterator2.next())) return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ImportingScope.extractSliceTower(names: Collection<Name>): Sequence<Collection<DeclarationDescriptor>> {
|
||||||
|
return parentsWithSelf
|
||||||
|
.map { scope ->
|
||||||
|
names.flatMap { name ->
|
||||||
|
scope.getContributedFunctions(name, NoLookupLocation.FROM_IDE) +
|
||||||
|
scope.getContributedVariables(name, NoLookupLocation.FROM_IDE) +
|
||||||
|
scope.getContributedClassifier(name, NoLookupLocation.FROM_IDE).singletonOrEmptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canUseStarImport(descriptor: DeclarationDescriptor, fqName: FqName): Boolean {
|
||||||
|
return when {
|
||||||
|
fqName.parent().isRoot -> false
|
||||||
|
(descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.OBJECT -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isImportedByDefault(fqName: FqName) = importInsertHelper.isImportedWithDefault(ImportPath(fqName, false), file)
|
||||||
|
|
||||||
|
private fun DeclarationDescriptor.nameCountToUseStar(): Int {
|
||||||
|
val isMember = containingDeclaration is ClassDescriptor
|
||||||
|
return if (isMember)
|
||||||
|
options.nameCountToUseStarImportForMembers
|
||||||
|
else
|
||||||
|
options.nameCountToUseStarImport
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun areTargetsEqual(descriptors1: Collection<DeclarationDescriptor>, descriptors2: Collection<DeclarationDescriptor>): Boolean {
|
||||||
|
return descriptors1.size == descriptors2.size &&
|
||||||
|
descriptors1.zip(descriptors2).all { it.first.importableFqName == it.second.importableFqName } //TODO: can have different order?
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,4 +53,6 @@ class KDocReference(element: KDocName): KtMultiReference<KDocName>(element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getCanonicalText(): String = element.getNameText()
|
override fun getCanonicalText(): String = element.getNameText()
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String> get() = listOf(element.getNameText())
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtArrayAccessExpression;
|
|||||||
import org.jetbrains.kotlin.psi.KtContainerNode;
|
import org.jetbrains.kotlin.psi.KtContainerNode;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -98,4 +99,12 @@ public class KtArrayAccessReference extends KtSimpleReference<KtArrayAccessExpre
|
|||||||
public PsiElement handleElementRename(@Nullable String newElementName) {
|
public PsiElement handleElementRename(@Nullable String newElementName) {
|
||||||
return ReferenceUtilKt.renameImplicitConventionalCall(this, newElementName);
|
return ReferenceUtilKt.renameImplicitConventionalCall(this, newElementName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final List<String> NAMES = Lists.newArrayList(OperatorNameConventions.GET.getIdentifier(), OperatorNameConventions.SET.getIdentifier());
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> getResolvesByNames() {
|
||||||
|
return NAMES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -17,8 +17,12 @@
|
|||||||
package org.jetbrains.kotlin.idea.references;
|
package org.jetbrains.kotlin.idea.references;
|
||||||
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
import com.intellij.openapi.util.TextRange;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression;
|
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
public class KtConstructorDelegationReference extends KtSimpleReference<KtConstructorDelegationReferenceExpression> {
|
public class KtConstructorDelegationReference extends KtSimpleReference<KtConstructorDelegationReferenceExpression> {
|
||||||
public KtConstructorDelegationReference(KtConstructorDelegationReferenceExpression expression) {
|
public KtConstructorDelegationReference(KtConstructorDelegationReferenceExpression expression) {
|
||||||
super(expression);
|
super(expression);
|
||||||
@@ -28,4 +32,10 @@ public class KtConstructorDelegationReference extends KtSimpleReference<KtConstr
|
|||||||
public TextRange getRangeInElement() {
|
public TextRange getRangeInElement() {
|
||||||
return new TextRange(0, getElement().getTextLength());
|
return new TextRange(0, getElement().getTextLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> getResolvesByNames() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
@@ -42,4 +43,10 @@ class KtDestructuringDeclarationReference(element: KtDestructuringDeclarationEnt
|
|||||||
if (canRename()) return expression
|
if (canRename()) return expression
|
||||||
throw IncorrectOperationException()
|
throw IncorrectOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String>
|
||||||
|
get() {
|
||||||
|
val componentIndex = (element.parent as KtDestructuringDeclaration).entries.indexOf(element) + 1
|
||||||
|
return listOf("component$componentIndex")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.TextRange
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtForExpression
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExpression>(element) {
|
class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExpression>(element) {
|
||||||
|
|
||||||
@@ -35,11 +36,20 @@ class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExp
|
|||||||
return LOOP_RANGE_KEYS.mapNotNull { key -> context.get(key, loopRange)?.candidateDescriptor }
|
return LOOP_RANGE_KEYS.mapNotNull { key -> context.get(key, loopRange)?.candidateDescriptor }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String>
|
||||||
|
get() = NAMES
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOOP_RANGE_KEYS = arrayOf(
|
private val LOOP_RANGE_KEYS = arrayOf(
|
||||||
BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL,
|
BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL,
|
||||||
BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL,
|
BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL,
|
||||||
BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL
|
BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val NAMES = listOf(
|
||||||
|
OperatorNameConventions.ITERATOR.identifier,
|
||||||
|
OperatorNameConventions.NEXT.identifier,
|
||||||
|
OperatorNameConventions.HAS_NEXT.identifier
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.references;
|
package org.jetbrains.kotlin.idea.references;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.openapi.util.TextRange;
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.MultiRangeReference;
|
import com.intellij.psi.MultiRangeReference;
|
||||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -112,4 +114,12 @@ public class KtInvokeFunctionReference extends KtSimpleReference<KtCallExpressio
|
|||||||
public PsiElement handleElementRename(@Nullable String newElementName) {
|
public PsiElement handleElementRename(@Nullable String newElementName) {
|
||||||
return ReferenceUtilKt.renameImplicitConventionalCall(this, newElementName);
|
return ReferenceUtilKt.renameImplicitConventionalCall(this, newElementName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final List<String> NAMES = Lists.newArrayList(OperatorNameConventions.INVOKE.getIdentifier());
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> getResolvesByNames() {
|
||||||
|
return NAMES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -21,9 +21,9 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
import org.jetbrains.kotlin.psi.KtPropertyDelegate
|
import org.jetbrains.kotlin.psi.KtPropertyDelegate
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import java.util.Collections
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
class KtPropertyDelegationMethodsReference(element: KtPropertyDelegate) : KtMultiReference<KtPropertyDelegate>(element) {
|
class KtPropertyDelegationMethodsReference(element: KtPropertyDelegate) : KtMultiReference<KtPropertyDelegate>(element) {
|
||||||
|
|
||||||
@@ -34,17 +34,17 @@ class KtPropertyDelegationMethodsReference(element: KtPropertyDelegate) : KtMult
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
|
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
|
||||||
val property = expression.getStrictParentOfType<KtProperty>()
|
val property = expression.getStrictParentOfType<KtProperty>() ?: return emptyList()
|
||||||
if (property == null) {
|
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? PropertyDescriptor ?: return emptyList()
|
||||||
return Collections.emptyList()
|
|
||||||
}
|
|
||||||
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property)
|
|
||||||
if (descriptor !is PropertyDescriptor) {
|
|
||||||
return Collections.emptyList()
|
|
||||||
}
|
|
||||||
return (descriptor.accessors.mapNotNull {
|
return (descriptor.accessors.mapNotNull {
|
||||||
accessor ->
|
accessor ->
|
||||||
context.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor)?.candidateDescriptor
|
context.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor)?.candidateDescriptor
|
||||||
} + listOfNotNull(context.get(BindingContext.DELEGATED_PROPERTY_PD_RESOLVED_CALL, descriptor)?.candidateDescriptor))
|
} + listOfNotNull(context.get(BindingContext.DELEGATED_PROPERTY_PD_RESOLVED_CALL, descriptor)?.candidateDescriptor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String> get() = NAMES
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val NAMES = listOf(OperatorNameConventions.GET_VALUE.identifier, OperatorNameConventions.SET_VALUE.identifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ interface KtReference : PsiPolyVariantReference {
|
|||||||
fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor>
|
fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor>
|
||||||
|
|
||||||
override fun getElement(): KtElement
|
override fun getElement(): KtElement
|
||||||
|
|
||||||
|
val resolvesByNames: Collection<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractKtReference<T : KtElement>(element: T)
|
abstract class AbstractKtReference<T : KtElement>(element: T)
|
||||||
|
|||||||
@@ -204,4 +204,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getCanonicalText(): String = expression.text
|
override fun getCanonicalText(): String = expression.text
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String>
|
||||||
|
get() = listOf(element.getReferencedName())
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -74,6 +74,9 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre
|
|||||||
return renameByPropertyName(newName.identifier)
|
return renameByPropertyName(newName.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val resolvesByNames: Collection<String>
|
||||||
|
get() = listOf(element.getReferencedName())
|
||||||
|
|
||||||
class Getter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, true)
|
class Getter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, true)
|
||||||
class Setter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, false)
|
class Setter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,17 +26,16 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo
|
import org.jetbrains.kotlin.idea.caches.resolve.getNullableModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
||||||
|
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
|
||||||
import org.jetbrains.kotlin.idea.references.KtReference
|
import org.jetbrains.kotlin.idea.references.KtReference
|
||||||
import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport
|
import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.idea.util.getFileResolutionScope
|
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.renderer.render
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
import org.jetbrains.kotlin.resolve.ImportPath
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||||
@@ -45,10 +44,9 @@ import org.jetbrains.kotlin.resolve.scopes.utils.*
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class KotlinImportOptimizer() : ImportOptimizer {
|
class KotlinImportOptimizer() : ImportOptimizer {
|
||||||
|
|
||||||
override fun supports(file: PsiFile?) = file is KtFile
|
override fun supports(file: PsiFile?) = file is KtFile
|
||||||
|
|
||||||
override fun processFile(file: PsiFile?) = Runnable() {
|
override fun processFile(file: PsiFile?) = Runnable {
|
||||||
OptimizeProcess(file as KtFile).execute()
|
OptimizeProcess(file as KtFile).execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +67,13 @@ class KotlinImportOptimizer() : ImportOptimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CollectUsedDescriptorsVisitor(val file: KtFile) : KtVisitorVoid() {
|
private class CollectUsedDescriptorsVisitor(file: KtFile) : KtVisitorVoid() {
|
||||||
private val _descriptors = HashSet<DeclarationDescriptor>()
|
|
||||||
private val currentPackageName = file.packageFqName
|
private val currentPackageName = file.packageFqName
|
||||||
|
private val descriptorsToImport = HashSet<DeclarationDescriptor>()
|
||||||
|
private val abstractRefs = ArrayList<OptimizedImportsBuilder.AbstractReference>()
|
||||||
|
|
||||||
val descriptors: Set<DeclarationDescriptor>
|
val data: OptimizedImportsBuilder.InputData
|
||||||
get() = _descriptors
|
get() = OptimizedImportsBuilder.InputData(descriptorsToImport, abstractRefs)
|
||||||
|
|
||||||
override fun visitElement(element: PsiElement) {
|
override fun visitElement(element: PsiElement) {
|
||||||
ProgressIndicatorProvider.checkCanceled()
|
ProgressIndicatorProvider.checkCanceled()
|
||||||
@@ -90,14 +89,16 @@ class KotlinImportOptimizer() : ImportOptimizer {
|
|||||||
override fun visitKtElement(element: KtElement) {
|
override fun visitKtElement(element: KtElement) {
|
||||||
for (reference in element.references) {
|
for (reference in element.references) {
|
||||||
if (reference !is KtReference) continue
|
if (reference !is KtReference) continue
|
||||||
|
abstractRefs.add(AbstractReferenceImpl(reference))
|
||||||
|
|
||||||
val referencedName = (element as? KtNameReferenceExpression)?.getReferencedNameAsName() //TODO: other types of references
|
val names = reference.resolvesByNames
|
||||||
|
|
||||||
val bindingContext = element.analyze()
|
val bindingContext = element.analyze()
|
||||||
//class qualifiers that refer to companion objects should be considered (containing) class references
|
val targets = reference.targets(bindingContext)
|
||||||
val targets = bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element as? KtReferenceExpression]?.let { listOf(it) }
|
|
||||||
?: reference.resolveToDescriptors(bindingContext)
|
|
||||||
for (target in targets) {
|
for (target in targets) {
|
||||||
|
val importableDescriptor = target.getImportableDescriptor()
|
||||||
|
if (importableDescriptor.name.asString() !in names) continue // resolved via alias
|
||||||
|
|
||||||
val importableFqName = target.importableFqName ?: continue
|
val importableFqName = target.importableFqName ?: continue
|
||||||
val parentFqName = importableFqName.parent()
|
val parentFqName = importableFqName.parent()
|
||||||
if (target is PackageViewDescriptor && parentFqName == FqName.ROOT) continue // no need to import top-level packages
|
if (target is PackageViewDescriptor && parentFqName == FqName.ROOT) continue // no need to import top-level packages
|
||||||
@@ -105,13 +106,9 @@ class KotlinImportOptimizer() : ImportOptimizer {
|
|||||||
|
|
||||||
if (!reference.canBeResolvedViaImport(target)) continue
|
if (!reference.canBeResolvedViaImport(target)) continue
|
||||||
|
|
||||||
val importableDescriptor = target.getImportableDescriptor()
|
|
||||||
|
|
||||||
if (referencedName != null && importableDescriptor.name != referencedName) continue // resolved via alias
|
|
||||||
|
|
||||||
if (isAccessibleAsMember(importableDescriptor, element, bindingContext)) continue
|
if (isAccessibleAsMember(importableDescriptor, element, bindingContext)) continue
|
||||||
|
|
||||||
_descriptors.add(importableDescriptor)
|
descriptorsToImport.add(importableDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,25 +142,43 @@ class KotlinImportOptimizer() : ImportOptimizer {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class AbstractReferenceImpl(private val reference: KtReference) : OptimizedImportsBuilder.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).map { Name.identifier(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolvesByNames.map { Name.identifier(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resolve(bindingContext: BindingContext) = reference.resolveToDescriptors(bindingContext)
|
||||||
|
|
||||||
|
override fun toString() = reference.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun collectDescriptorsToImport(file: KtFile): Set<DeclarationDescriptor> {
|
fun collectDescriptorsToImport(file: KtFile): OptimizedImportsBuilder.InputData {
|
||||||
val visitor = CollectUsedDescriptorsVisitor(file)
|
val visitor = CollectUsedDescriptorsVisitor(file)
|
||||||
file.accept(visitor)
|
file.accept(visitor)
|
||||||
return visitor.descriptors
|
return visitor.data
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prepareOptimizedImports(file: KtFile,
|
fun prepareOptimizedImports(file: KtFile, data: OptimizedImportsBuilder.InputData): List<ImportPath>? {
|
||||||
descriptorsToImport: Collection<DeclarationDescriptor>): List<ImportPath>? {
|
|
||||||
val settings = KotlinCodeStyleSettings.getInstance(file.project)
|
val settings = KotlinCodeStyleSettings.getInstance(file.project)
|
||||||
return prepareOptimizedImports(
|
val options = OptimizedImportsBuilder.Options(
|
||||||
file,
|
|
||||||
descriptorsToImport,
|
|
||||||
settings.NAME_COUNT_TO_USE_STAR_IMPORT,
|
settings.NAME_COUNT_TO_USE_STAR_IMPORT,
|
||||||
settings.NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS) { fqName ->
|
settings.NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS,
|
||||||
fqName.asString() in settings.PACKAGES_TO_USE_STAR_IMPORTS
|
isInPackagesToUseStarImport = { fqName -> fqName.asString() in settings.PACKAGES_TO_USE_STAR_IMPORTS })
|
||||||
}
|
return OptimizedImportsBuilder(file, data, options).buildOptimizedImports()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceImports(file: KtFile, imports: List<ImportPath>) {
|
fun replaceImports(file: KtFile, imports: List<ImportPath>) {
|
||||||
@@ -179,5 +194,11 @@ class KotlinImportOptimizer() : ImportOptimizer {
|
|||||||
import.delete()
|
import.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
?: resolveToDescriptors(bindingContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ import com.intellij.psi.PsiDocumentManager
|
|||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.util.DocumentUtil
|
import com.intellij.util.DocumentUtil
|
||||||
import com.intellij.util.Processor
|
import com.intellij.util.Processor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||||
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
|
import org.jetbrains.kotlin.idea.imports.KotlinImportOptimizer
|
||||||
|
import org.jetbrains.kotlin.idea.imports.OptimizedImportsBuilder
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
@@ -59,7 +59,7 @@ class KotlinUnusedImportInspection : AbstractKotlinInspection() {
|
|||||||
if (!file.manager.isInProject(file)) return null
|
if (!file.manager.isInProject(file)) return null
|
||||||
if (file.importDirectives.isEmpty()) return null
|
if (file.importDirectives.isEmpty()) return null
|
||||||
|
|
||||||
val descriptorsToImport = KotlinImportOptimizer.collectDescriptorsToImport(file)
|
val data = KotlinImportOptimizer.collectDescriptorsToImport(file)
|
||||||
|
|
||||||
val directives = file.importDirectives
|
val directives = file.importDirectives
|
||||||
val explicitlyImportedFqNames = directives
|
val explicitlyImportedFqNames = directives
|
||||||
@@ -71,7 +71,7 @@ class KotlinUnusedImportInspection : AbstractKotlinInspection() {
|
|||||||
|
|
||||||
val fqNames = HashSet<FqName>()
|
val fqNames = HashSet<FqName>()
|
||||||
val parentFqNames = HashSet<FqName>()
|
val parentFqNames = HashSet<FqName>()
|
||||||
for (descriptor in descriptorsToImport) {
|
for (descriptor in data.descriptorsToImport) {
|
||||||
val fqName = descriptor.importableFqName!!
|
val fqName = descriptor.importableFqName!!
|
||||||
fqNames.add(fqName)
|
fqNames.add(fqName)
|
||||||
|
|
||||||
@@ -119,15 +119,15 @@ class KotlinUnusedImportInspection : AbstractKotlinInspection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isOnTheFly) {
|
if (isOnTheFly) {
|
||||||
scheduleOptimizeImportsOnTheFly(file, descriptorsToImport)
|
scheduleOptimizeImportsOnTheFly(file, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return problems.toTypedArray()
|
return problems.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleOptimizeImportsOnTheFly(file: KtFile, descriptorsToImport: Set<DeclarationDescriptor>) {
|
private fun scheduleOptimizeImportsOnTheFly(file: KtFile, data: OptimizedImportsBuilder.InputData) {
|
||||||
if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return
|
if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return
|
||||||
val optimizedImports = KotlinImportOptimizer.prepareOptimizedImports(file, descriptorsToImport) ?: return // return if already optimized
|
val optimizedImports = KotlinImportOptimizer.prepareOptimizedImports(file, data) ?: return // return if already optimized
|
||||||
|
|
||||||
// unwrap progress indicator
|
// unwrap progress indicator
|
||||||
val progress = generateSequence(ProgressManager.getInstance().progressIndicator) {
|
val progress = generateSequence(ProgressManager.getInstance().progressIndicator) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Additional checking of reference Getter: E2
|
||||||
|
Additional checking of reference KtSimpleNameReference: E2
|
||||||
|
Additional checking of reference Getter: E22
|
||||||
|
Additional checking of reference KtSimpleNameReference: E22
|
||||||
|
Additional checking of reference Getter: E31
|
||||||
|
Additional checking of reference KtSimpleNameReference: E31
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Additional checking of reference Getter: run
|
||||||
|
Additional checking of reference KtSimpleNameReference: run
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: run("")
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package bug.a
|
||||||
|
|
||||||
|
class A(val foo: MyFunction)
|
||||||
|
|
||||||
|
class MyFunction
|
||||||
|
operator fun MyFunction.invoke() = println("invoke convention")
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||||
|
package bug.b
|
||||||
|
|
||||||
|
import bug.a.*
|
||||||
|
import bug.a.invoke
|
||||||
|
|
||||||
|
fun A.foo() = println("extension function")
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A(MyFunction())
|
||||||
|
|
||||||
|
a.foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||||
|
package bug.b
|
||||||
|
|
||||||
|
import bug.a.*
|
||||||
|
import bug.a.invoke
|
||||||
|
|
||||||
|
fun A.foo() = println("extension function")
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val a = A(MyFunction())
|
||||||
|
|
||||||
|
a.foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Additional checking of reference KtInvokeFunctionReference: A(MyFunction())
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: MyFunction()
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: foo()
|
||||||
|
Changed resolve of KtInvokeFunctionReference: foo()
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: println("extension function")
|
||||||
|
Trying to build import list again with locked imports: bug.a.invoke
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package bug.a
|
||||||
|
|
||||||
|
class A(val foo: MyFunction)
|
||||||
|
|
||||||
|
class MyFunction
|
||||||
|
operator fun MyFunction.invoke() = println("invoke convention")
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 10
|
||||||
|
package bug.b
|
||||||
|
|
||||||
|
import bug.a.*
|
||||||
|
|
||||||
|
fun A.foo() = println("extension function")
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val func = MyFunction()
|
||||||
|
func()
|
||||||
|
|
||||||
|
val a = A(func)
|
||||||
|
|
||||||
|
a.foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 10
|
||||||
|
package bug.b
|
||||||
|
|
||||||
|
import bug.a.*
|
||||||
|
|
||||||
|
fun A.foo() = println("extension function")
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val func = MyFunction()
|
||||||
|
func()
|
||||||
|
|
||||||
|
val a = A(func)
|
||||||
|
|
||||||
|
a.foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Additional checking of reference KtInvokeFunctionReference: foo()
|
||||||
|
Changed resolve of KtInvokeFunctionReference: foo()
|
||||||
|
Trying to build import list again with locked imports: bug.a.*
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Additional checking of reference Getter: Inner2
|
||||||
|
Additional checking of reference KtSimpleNameReference: Inner2
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: Inner2()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package pack1
|
||||||
|
|
||||||
|
fun foo(o: Any){}
|
||||||
|
|
||||||
|
val u1 = 1
|
||||||
|
val u2 = 1
|
||||||
|
val u3 = 1
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package pack2
|
||||||
|
|
||||||
|
fun foo(o: String){}
|
||||||
|
|
||||||
|
val v1 = 1
|
||||||
|
val v2 = 1
|
||||||
|
val v3 = 1
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 3
|
||||||
|
|
||||||
|
import pack1.foo
|
||||||
|
import pack1.u1
|
||||||
|
import pack1.u2
|
||||||
|
import pack1.u3
|
||||||
|
import pack1.u4
|
||||||
|
import pack1.u5
|
||||||
|
import pack2.*
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
foo("")
|
||||||
|
v1 + v2 + v3
|
||||||
|
u1 + u2 + u3
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 3
|
||||||
|
|
||||||
|
import pack1.*
|
||||||
|
import pack1.foo
|
||||||
|
import pack2.*
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
foo("")
|
||||||
|
v1 + v2 + v3
|
||||||
|
u1 + u2 + u3
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Additional checking of reference Getter: foo
|
||||||
|
Additional checking of reference KtSimpleNameReference: foo
|
||||||
|
Changed resolve of KtSimpleNameReference: foo
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: foo("")
|
||||||
|
Trying to build import list again with locked imports: pack1.foo, pack2.*
|
||||||
|
Additional checking of reference Getter: foo
|
||||||
|
Additional checking of reference KtSimpleNameReference: foo
|
||||||
|
Additional checking of reference KtInvokeFunctionReference: foo("")
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package p1
|
||||||
|
|
||||||
|
class A(p: Int)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package p2
|
||||||
|
|
||||||
|
class A(s: String)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||||
|
import p1.*
|
||||||
|
import p2.A
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
A(1)
|
||||||
|
A("")
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// NAME_COUNT_TO_USE_STAR_IMPORT: 2
|
||||||
|
import p1.*
|
||||||
|
import p2.A
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
A(1)
|
||||||
|
A("")
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Additional checking of reference Getter: A
|
||||||
|
Additional checking of reference Getter: A
|
||||||
|
Additional checking of reference KtSimpleNameReference: A
|
||||||
|
Changed resolve of KtSimpleNameReference: A
|
||||||
|
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 locked imports: p2.A, p1.*
|
||||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin
|
|||||||
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
|
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
|
||||||
import com.intellij.psi.codeStyle.PackageEntry
|
import com.intellij.psi.codeStyle.PackageEntry
|
||||||
|
import junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
@@ -40,13 +41,12 @@ abstract class AbstractImportsTest : KotlinLightCodeInsightFixtureTestCase() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val fixture = myFixture
|
val fixture = myFixture
|
||||||
val dependencyPath = testPath.replace(".kt", ".dependency.kt")
|
val dependencySuffixes = listOf(".dependency.kt", ".dependency.java", ".dependency1.kt", ".dependency2.kt")
|
||||||
if (File(dependencyPath).exists()) {
|
for (suffix in dependencySuffixes) {
|
||||||
fixture.configureByFile(dependencyPath)
|
val dependencyPath = testPath.replace(".kt", suffix)
|
||||||
}
|
if (File(dependencyPath).exists()) {
|
||||||
val javaDependencyPath = testPath.replace(".kt", ".dependency.java")
|
fixture.configureByFile(dependencyPath)
|
||||||
if (File(javaDependencyPath).exists()) {
|
}
|
||||||
fixture.configureByFile(javaDependencyPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fixture.configureByFile(testPath)
|
fixture.configureByFile(testPath)
|
||||||
@@ -66,18 +66,26 @@ abstract class AbstractImportsTest : KotlinLightCodeInsightFixtureTestCase() {
|
|||||||
codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), true))
|
codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), true))
|
||||||
}
|
}
|
||||||
|
|
||||||
project.executeWriteCommand("") {
|
val log = project.executeWriteCommand<String?>("") { doTest(file) }
|
||||||
doTest(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(File(testPath + ".after"), myFixture.file.text)
|
KotlinTestUtils.assertEqualsToFile(File(testPath + ".after"), myFixture.file.text)
|
||||||
|
if (log != null) {
|
||||||
|
val logFile = File(testPath + ".log")
|
||||||
|
if (log.isNotEmpty()) {
|
||||||
|
KotlinTestUtils.assertEqualsToFile(logFile, log)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TestCase.assertFalse(logFile.exists())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
settingManager.dropTemporarySettings()
|
settingManager.dropTemporarySettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun doTest(file: KtFile)
|
// returns test log
|
||||||
|
protected abstract fun doTest(file: KtFile): String?
|
||||||
|
|
||||||
protected open val nameCountToUseStarImportDefault: Int
|
protected open val nameCountToUseStarImportDefault: Int
|
||||||
get() = 1
|
get() = 1
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
|||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
|
||||||
abstract class AbstractAddImportTest : AbstractImportsTest() {
|
abstract class AbstractAddImportTest : AbstractImportsTest() {
|
||||||
override fun doTest(file: KtFile) {
|
override fun doTest(file: KtFile): String? {
|
||||||
var descriptorName = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// IMPORT:")
|
var descriptorName = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// IMPORT:")
|
||||||
?: error("No IMPORT directive defined")
|
?: error("No IMPORT directive defined")
|
||||||
|
|
||||||
@@ -57,5 +57,7 @@ abstract class AbstractAddImportTest : AbstractImportsTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,17 +16,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.imports
|
package org.jetbrains.kotlin.idea.imports
|
||||||
|
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import org.jetbrains.kotlin.AbstractImportsTest
|
||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import java.io.File
|
|
||||||
import org.junit.Assert
|
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
|
||||||
import org.jetbrains.kotlin.*
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
|
|
||||||
abstract class AbstractOptimizeImportsTest() : AbstractImportsTest() {
|
abstract class AbstractOptimizeImportsTest() : AbstractImportsTest() {
|
||||||
override fun doTest(file: KtFile) {
|
override fun doTest(file: KtFile): String {
|
||||||
KotlinImportOptimizer().processFile(file).run()
|
OptimizedImportsBuilder.testLog = StringBuilder()
|
||||||
|
try {
|
||||||
|
KotlinImportOptimizer().processFile(file).run()
|
||||||
|
return OptimizedImportsBuilder.testLog.toString()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
OptimizedImportsBuilder.testLog = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val nameCountToUseStarImportDefault: Int
|
override val nameCountToUseStarImportDefault: Int
|
||||||
|
|||||||
@@ -155,6 +155,18 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("KT11640.kt")
|
||||||
|
public void testKT11640() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/KT11640.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("KT11640_1.kt")
|
||||||
|
public void testKT11640_1() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/KT11640_1.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("KT13766.kt")
|
@TestMetadata("KT13766.kt")
|
||||||
public void testKT13766() throws Exception {
|
public void testKT13766() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/KT13766.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/KT13766.kt");
|
||||||
@@ -215,6 +227,12 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Overloads.kt")
|
||||||
|
public void testOverloads() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/Overloads.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("RemoveImportsIfGeneral.kt")
|
@TestMetadata("RemoveImportsIfGeneral.kt")
|
||||||
public void testRemoveImportsIfGeneral() throws Exception {
|
public void testRemoveImportsIfGeneral() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/RemoveImportsIfGeneral.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/RemoveImportsIfGeneral.kt");
|
||||||
@@ -251,6 +269,12 @@ public class OptimizeImportsTestGenerated extends AbstractOptimizeImportsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("TwoConstructors.kt")
|
||||||
|
public void testTwoConstructors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/TwoConstructors.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("TypeAliasUsage.kt")
|
@TestMetadata("TypeAliasUsage.kt")
|
||||||
public void testTypeAliasUsage() throws Exception {
|
public void testTypeAliasUsage() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/TypeAliasUsage.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/optimizeImports/TypeAliasUsage.kt");
|
||||||
|
|||||||
@@ -16,17 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.shortenRefs
|
package org.jetbrains.kotlin.shortenRefs
|
||||||
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
|
||||||
import org.jetbrains.kotlin.AbstractImportsTest
|
import org.jetbrains.kotlin.AbstractImportsTest
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
abstract class AbstractShortenRefsTest : AbstractImportsTest() {
|
abstract class AbstractShortenRefsTest : AbstractImportsTest() {
|
||||||
override fun doTest(file: KtFile) {
|
override fun doTest(file: KtFile): String? {
|
||||||
val selectionModel = myFixture.editor.selectionModel
|
val selectionModel = myFixture.editor.selectionModel
|
||||||
if (!selectionModel.hasSelection()) error("No selection in input file")
|
if (!selectionModel.hasSelection()) error("No selection in input file")
|
||||||
ShortenReferences { ShortenReferences.Options(removeThis = true, removeThisLabels = true) }
|
ShortenReferences { ShortenReferences.Options(removeThis = true, removeThisLabels = true) }
|
||||||
.process(file, selectionModel.selectionStart, selectionModel.selectionEnd)
|
.process(file, selectionModel.selectionStart, selectionModel.selectionEnd)
|
||||||
selectionModel.removeSelection()
|
selectionModel.removeSelection()
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override val nameCountToUseStarImportDefault: Int
|
override val nameCountToUseStarImportDefault: Int
|
||||||
|
|||||||
Reference in New Issue
Block a user