Minor refactoring: destructure intention

This commit is contained in:
Mikhail Glukhikh
2016-10-11 16:03:50 +03:00
parent c6fefbc223
commit 206374cd17
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.utils.singletonOrEmptyList
import java.util.*
@@ -48,9 +47,9 @@ class DestructureInspection : IntentionBasedInspection<KtDeclaration>(
DestructureIntention::class,
{ element, inspection ->
if (element is KtParameter) true
else with(inspection as DestructureInspection) {
val usagesToRemove = DestructureIntention.collectUsagesToRemove(element, element.parent as? KtForExpression)?.data
usagesToRemove != null && usagesToRemove.any { it.declarationToDrop is KtDestructuringDeclaration }
else {
val usagesToRemove = DestructureIntention.collectUsagesToRemove(element)?.data
usagesToRemove?.any { it.declarationToDrop is KtDestructuringDeclaration } ?: false
}
}
)
@@ -60,13 +59,11 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
"Use destructuring declaration"
) {
override fun applyTo(element: KtDeclaration, editor: Editor?) {
val forLoop = element.parent as? KtForExpression
val loopRange = forLoop?.loopRange
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop)!!
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element)!!
val factory = KtPsiFactory(element)
val validator = NewDeclarationNameValidator(
element.parent, element, NewDeclarationNameValidator.Target.VARIABLES,
container = element.parent, anchor = element, target = NewDeclarationNameValidator.Target.VARIABLES,
excludedDeclarations = usagesToRemove.map { it.declarationToDrop.singletonOrEmptyList() }.flatten()
)
val names = ArrayList<String>()
@@ -82,6 +79,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
val joinedNames = names.joinToString()
when (element) {
is KtParameter -> {
val loopRange = (element.parent as? KtForExpression)?.loopRange
element.replace(factory.createDestructuringParameter("($joinedNames)"))
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
loopRange.replace(loopRange.receiverExpression)
@@ -113,9 +111,9 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
}
override fun applicabilityRange(element: KtDeclaration): TextRange? {
if (!isSuitableDeclaration(element)) return null
if (!element.isSuitableDeclaration()) return null
val usagesToRemove = collectUsagesToRemove(element, element.parent as? KtForExpression)?.data ?: return null
val usagesToRemove = collectUsagesToRemove(element)?.data ?: return null
if (usagesToRemove.isEmpty()) return null
return when (element) {
@@ -125,33 +123,36 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
}
}
private fun isSuitableDeclaration(declaration: KtDeclaration) = when (declaration) {
is KtParameter -> {
val parent = declaration.parent
when {
parent is KtForExpression -> true
parent?.parent is KtFunctionLiteral -> true
else -> false
}
}
is KtVariableDeclaration -> true
is KtFunctionLiteral -> !declaration.hasParameterSpecification() // replace implicit 'it' with destructuring declaration
else -> false
}
companion object {
internal fun KtDeclaration.isSuitableDeclaration() = getUsageScopeElement() != null
private fun KtDeclaration.getUsageScopeElement() = when (this) {
is KtParameter -> {
val parent = parent
when {
parent is KtForExpression -> parent
parent?.parent is KtFunctionLiteral -> parent.parent
else -> null
}
}
is KtVariableDeclaration -> parent
is KtFunctionLiteral -> if (!hasParameterSpecification()) this else null
else -> null
}
internal data class UsagesToRemove(val data: List<UsageData>, val removeSelectorInLoopRange: Boolean)
internal fun collectUsagesToRemove(declaration: KtDeclaration, forLoop: KtForExpression?): UsagesToRemove? {
internal fun collectUsagesToRemove(declaration: KtDeclaration): UsagesToRemove? {
val context = declaration.analyze()
val variableDescriptor = when (declaration) {
is KtParameter -> context.get(BindingContext.VALUE_PARAMETER, declaration)
is KtFunctionLiteral -> context.get(BindingContext.FUNCTION, declaration)?.valueParameters?.singleOrNull()
is KtVariableDeclaration -> context.get(BindingContext.VARIABLE, declaration)
else -> null
} ?: return null
val variableDescriptor =
when (declaration) {
is KtParameter -> context.get(BindingContext.VALUE_PARAMETER, declaration)
is KtFunctionLiteral -> context.get(BindingContext.FUNCTION, declaration)?.valueParameters?.singleOrNull()
is KtVariableDeclaration -> context.get(BindingContext.VARIABLE, declaration)
else -> null
} ?: return null
val variableType = variableDescriptor.type
if (variableType.isMarkedNullable) return null
@@ -163,6 +164,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
val usagesToRemove = mutableListOf<UsageData>()
var noBadUsages = true
var removeSelectorInLoopRange = false
val forLoop = declaration.parent as? KtForExpression
if (forLoop != null && DescriptorUtils.isSubclass(classDescriptor, mapEntryClassDescriptor)) {
val loopRangeDescriptor = forLoop.loopRange.getResolvedCall(context)?.resultingDescriptor
if (loopRangeDescriptor != null) {
@@ -175,7 +177,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
}
listOf("key", "value").mapTo(usagesToRemove) {
UsageData(mapEntryClassDescriptor.unsubstitutedMemberScope.getContributedVariables(
UsageData(descriptor = mapEntryClassDescriptor.unsubstitutedMemberScope.getContributedVariables(
Name.identifier(it), NoLookupLocation.FROM_BUILTINS).single())
}
@@ -188,14 +190,9 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
else if (classDescriptor.isData) {
val valueParameters = classDescriptor.unsubstitutedPrimaryConstructor?.valueParameters ?: return null
valueParameters.mapTo(usagesToRemove) { UsageData(it) }
valueParameters.mapTo(usagesToRemove) { UsageData(descriptor = it) }
// inference bug: remove as? PsiElement when fixed
val usageScopeElement: PsiElement = forLoop as? PsiElement
?: (declaration as? KtFunctionLiteral)
?: (declaration.parent?.parent as? KtFunctionLiteral)
?: (declaration as? KtVariableDeclaration)?.parent
?: return null
val usageScopeElement = declaration.getUsageScopeElement() ?: return null
val nameToSearch = when (declaration) {
is KtParameter -> declaration.nameAsName
@@ -253,7 +250,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
}
cancel()
return@forEach false
false
})
}
@@ -325,7 +322,7 @@ class DestructureIntention : SelfTargetingRangeIntention<KtDeclaration>(
val descriptor: CallableDescriptor,
val usagesToReplace: MutableList<KtExpression> = mutableListOf(),
var declarationToDrop: KtDeclaration? = null,
var name: String? = declarationToDrop?.name
var name: String? = null
) {
// Returns true if data is successfully added, false otherwise
fun add(newData: SingleUsageData, componentIndex: Int): Boolean {