Change Signature: Process implicit usages of data class 'componentN' functions
#KT-9393 Fixed
This commit is contained in:
@@ -45,6 +45,7 @@ public open class KtClass : KtClassOrObject {
|
||||
_stub?.isInterface() ?: (findChildByType<PsiElement>(KtTokens.INTERFACE_KEYWORD) != null)
|
||||
|
||||
public fun isEnum(): Boolean = hasModifier(KtTokens.ENUM_KEYWORD)
|
||||
public fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)
|
||||
public fun isSealed(): Boolean = hasModifier(KtTokens.SEALED_KEYWORD)
|
||||
public fun isInner(): Boolean = hasModifier(KtTokens.INNER_KEYWORD)
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ public fun KtPsiFactory.createExpressionByPattern(pattern: String, vararg args:
|
||||
public fun <TDeclaration : KtDeclaration> KtPsiFactory.createDeclarationByPattern(pattern: String, vararg args: Any): TDeclaration
|
||||
= createByPattern(pattern, *args) { createDeclaration<TDeclaration>(it) }
|
||||
|
||||
public fun KtPsiFactory.createDestructuringDeclarationByPattern(pattern: String, vararg args: Any): KtDestructuringDeclaration
|
||||
= createByPattern(pattern, *args) { createDestructuringDeclaration(it) }
|
||||
|
||||
private abstract class ArgumentType<T : Any>(val klass: Class<T>)
|
||||
|
||||
private class PlainTextArgumentType<T : Any>(klass: Class<T>, val toPlainText: (T) -> String) : ArgumentType<T>(klass)
|
||||
@@ -315,6 +318,10 @@ public fun KtPsiFactory.buildDeclaration(build: BuilderByPattern<KtDeclaration>.
|
||||
return buildByPattern({ pattern, args -> this.createDeclarationByPattern(pattern, *args) }, build)
|
||||
}
|
||||
|
||||
public fun KtPsiFactory.buildDestructuringDeclaration(build: BuilderByPattern<KtDestructuringDeclaration>.() -> Unit): KtDestructuringDeclaration {
|
||||
return buildByPattern({ pattern, args -> this.createDestructuringDeclarationByPattern(pattern, *args) }, build)
|
||||
}
|
||||
|
||||
public fun <TElement> buildByPattern(factory: (String, Array<out Any>) -> TElement, build: BuilderByPattern<TElement>.() -> Unit): TElement {
|
||||
val builder = BuilderByPattern<TElement>()
|
||||
builder.build()
|
||||
|
||||
+25
-7
@@ -64,10 +64,7 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getValueParameters
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -77,6 +74,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.util.*
|
||||
|
||||
@@ -268,17 +266,28 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
|
||||
|
||||
val newReceiverInfo = changeInfo.receiverParameterInfo
|
||||
|
||||
for (parameterInfo in changeInfo.newParameters) {
|
||||
val isDataClass = functionPsi is KtPrimaryConstructor && (functionPsi.getContainingClassOrObject() as? KtClass)?.isData() ?: false
|
||||
|
||||
for ((i, parameterInfo) in changeInfo.newParameters.withIndex()) {
|
||||
if (parameterInfo.oldIndex >= 0 && parameterInfo.oldIndex < oldParameters.size) {
|
||||
val oldParam = oldParameters[parameterInfo.oldIndex]
|
||||
val oldParamName = oldParam.name
|
||||
|
||||
if (parameterInfo == newReceiverInfo || (oldParamName != null && oldParamName != parameterInfo.name)) {
|
||||
if (parameterInfo == newReceiverInfo ||
|
||||
(oldParamName != null && oldParamName != parameterInfo.name) ||
|
||||
isDataClass && i != parameterInfo.oldIndex) {
|
||||
for (reference in ReferencesSearch.search(oldParam, oldParam.useScope)) {
|
||||
val element = reference.element
|
||||
|
||||
if (isDataClass &&
|
||||
element is KtSimpleNameExpression &&
|
||||
(element.parent as? KtCallExpression)?.calleeExpression == element &&
|
||||
element.getReferencedName() != parameterInfo.name &&
|
||||
OperatorNameConventions.COMPONENT_REGEX.matches(element.getReferencedName())) {
|
||||
result.add(KotlinDataClassComponentUsage(element, "component${i + 1}"))
|
||||
}
|
||||
// Usages in named arguments of the calls usage will be changed when the function call is changed
|
||||
if ((element is KtSimpleNameExpression || element is KDocName) && element.parent !is KtValueArgumentName) {
|
||||
else if ((element is KtSimpleNameExpression || element is KDocName) && element.parent !is KtValueArgumentName) {
|
||||
result.add(KotlinParameterUsage(element as KtElement, parameterInfo, functionUsageInfo))
|
||||
}
|
||||
}
|
||||
@@ -286,6 +295,15 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDataClass) {
|
||||
(functionPsi as KtPrimaryConstructor).valueParameters.firstOrNull()?.let {
|
||||
ReferencesSearch.search(it).mapNotNullTo(result) {
|
||||
val destructuringDeclaration = it.element as? KtDestructuringDeclaration ?: return@mapNotNullTo null
|
||||
KotlinComponentUsageInDestructuring(destructuringDeclaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (functionPsi is KtFunction && newReceiverInfo != changeInfo.methodDescriptor.receiver) {
|
||||
findOriginalReceiversUsages(functionUsageInfo, result, changeInfo)
|
||||
}
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.refactoring.changeSignature.usages
|
||||
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator.Target
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.replaceListPsiAndKeepDelimiters
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.buildDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
class KotlinComponentUsageInDestructuring(element: KtDestructuringDeclaration) : KotlinUsageInfo<KtDestructuringDeclaration>(element) {
|
||||
override fun processUsage(changeInfo: KotlinChangeInfo, element: KtDestructuringDeclaration, allUsages: Array<out UsageInfo>): Boolean {
|
||||
if (!changeInfo.isParameterSetOrOrderChanged) return true
|
||||
|
||||
val currentEntries = element.entries
|
||||
val newParameterInfos = changeInfo.getNonReceiverParameters()
|
||||
|
||||
val newDestructuring = KtPsiFactory(element).buildDestructuringDeclaration {
|
||||
val lastIndex = newParameterInfos.indexOfLast { it.oldIndex in currentEntries.indices }
|
||||
val nameValidator = CollectingNameValidator(filter = NewDeclarationNameValidator(element.parent, null, Target.VARIABLES))
|
||||
|
||||
appendFixedText("val (")
|
||||
for (i in 0..lastIndex) {
|
||||
if (i > 0) {
|
||||
appendFixedText(", ")
|
||||
}
|
||||
|
||||
val paramInfo = newParameterInfos[i]
|
||||
val oldIndex = paramInfo.oldIndex
|
||||
if (oldIndex >= 0 && oldIndex < currentEntries.size) {
|
||||
appendChildRange(PsiChildRange.singleElement(currentEntries[oldIndex]))
|
||||
}
|
||||
else {
|
||||
appendFixedText(KotlinNameSuggester.suggestNameByName(paramInfo.name, nameValidator))
|
||||
}
|
||||
}
|
||||
appendFixedText(")")
|
||||
}
|
||||
replaceListPsiAndKeepDelimiters(
|
||||
element,
|
||||
newDestructuring,
|
||||
{
|
||||
apply {
|
||||
val oldEntries = entries.ifEmpty { return@apply }
|
||||
val firstOldEntry = oldEntries.first()
|
||||
val lastOldEntry = oldEntries.last()
|
||||
val newEntries = it.entries
|
||||
if (newEntries.isNotEmpty()) {
|
||||
addRangeBefore(newEntries.first(), newEntries.last(), firstOldEntry)
|
||||
}
|
||||
deleteChildRange(firstOldEntry, lastOldEntry)
|
||||
}
|
||||
},
|
||||
{ entries }
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.refactoring.changeSignature.usages
|
||||
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
class KotlinDataClassComponentUsage(
|
||||
calleeExpression: KtSimpleNameExpression,
|
||||
private val newName: String
|
||||
) : KotlinUsageInfo<KtSimpleNameExpression>(calleeExpression) {
|
||||
override fun processUsage(changeInfo: KotlinChangeInfo, element: KtSimpleNameExpression, allUsages: Array<out UsageInfo>): Boolean {
|
||||
element.replace(KtPsiFactory(element).createExpression(newName))
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -722,6 +722,7 @@ fun PsiNamedElement.isInterfaceClass(): Boolean = this is KtClass && isInterface
|
||||
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
||||
originalList: ListType,
|
||||
newList: ListType,
|
||||
@Suppress("UNCHECKED_CAST") listReplacer: ListType.(ListType) -> ListType = { replace(it) as ListType },
|
||||
itemsFun: ListType.() -> List<KtElement>
|
||||
): ListType {
|
||||
originalList.children.takeWhile { it is PsiErrorElement }.forEach { it.delete() }
|
||||
@@ -736,17 +737,13 @@ fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
||||
oldParameters[i] = oldParameters[i].replace(newParameters[i]) as KtElement
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (commonCount == 0) return originalList.replace(newList) as ListType
|
||||
if (commonCount == 0) return originalList.listReplacer(newList)
|
||||
|
||||
if (oldCount > commonCount) {
|
||||
originalList.deleteChildRange(oldParameters[commonCount - 1].nextSibling, oldParameters.last())
|
||||
}
|
||||
else if (newCount > commonCount) {
|
||||
originalList.addRangeAfter(newParameters[commonCount - 1].nextSibling,
|
||||
newList.lastChild.prevSibling,
|
||||
PsiTreeUtil.skipSiblingsBackward(originalList.lastChild,
|
||||
PsiWhiteSpace::class.java, PsiComment::class.java))
|
||||
originalList.addRangeAfter(newParameters[commonCount - 1].nextSibling, newParameters.last(), oldParameters.last())
|
||||
}
|
||||
|
||||
return originalList
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
data class <caret>X(val a: Int, val c: Int, val b: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, c1, b) = X(1, 3, 2)
|
||||
val aa = X(1, 3, 2).component1()
|
||||
val bb = X(1, 3, 2).component3()
|
||||
for ((a, c, b) in listOf(X(1, 3, 2))) {}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
data class <caret>X(val a: Int, val b: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, b) = X(1, 2)
|
||||
val aa = X(1, 2).component1()
|
||||
val bb = X(1, 2).component2()
|
||||
for ((a, b) in listOf(X(1, 2))) {}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val d: Int, val c: Int, val e: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val () = X(4, 3, 5)
|
||||
val aa = X(4, 3, 5).component1()
|
||||
val bb = X(4, 3, 5).component2()
|
||||
val cc = X(4, 3, 5).component2()
|
||||
for (() in listOf(X(4, 3, 5))) {}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val a: Int, val b: Int, val c: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, b) = X(1, 2, 3)
|
||||
val aa = X(1, 2, 3).component1()
|
||||
val bb = X(1, 2, 3).component2()
|
||||
val cc = X(1, 2, 3).component3()
|
||||
for ((a, b) in listOf(X(1, 2, 3))) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val a: Int, val c: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, c) = X(1, 3)
|
||||
val aa = X(1, 3).component1()
|
||||
val bb = X(1, 3).component2()
|
||||
val cc = X(1, 3).component2()
|
||||
for ((a, c) in listOf(X(1, 3))) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val a: Int, val b: Int, val c: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, b, c) = X(1, 2, 3)
|
||||
val aa = X(1, 2, 3).component1()
|
||||
val bb = X(1, 2, 3).component2()
|
||||
val cc = X(1, 2, 3).component3()
|
||||
for ((a, b, c) in listOf(X(1, 2, 3))) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val c: Int, val a: Int, val b: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (c, a, b) = X(3, 1, 2)
|
||||
val aa = X(3, 1, 2).component2()
|
||||
val bb = X(3, 1, 2).component3()
|
||||
val cc = X(3, 1, 2).component1()
|
||||
for ((c, a, b) in listOf(X(3, 1, 2))) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
data class <caret>X(val a: Int, val b: Int, val c: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val (a, b, c) = X(1, 2, 3)
|
||||
val aa = X(1, 2, 3).component1()
|
||||
val bb = X(1, 2, 3).component2()
|
||||
val cc = X(1, 2, 3).component3()
|
||||
for ((a, b, c) in listOf(X(1, 2, 3))) {}
|
||||
}
|
||||
+41
@@ -903,4 +903,45 @@ class KotlinChangeSignatureTest : KotlinCodeInsightTestCase() {
|
||||
fun testSetErrorReceiverType() = doTest { receiverParameterInfo!!.currentTypeInfo = KotlinTypeInfo(true, null, "XYZ") }
|
||||
|
||||
fun testSetErrorParameterType() = doTest { newParameters[1].currentTypeInfo = KotlinTypeInfo(true, null, "XYZ") }
|
||||
|
||||
fun testSwapDataClassParameters() = doTest {
|
||||
swapParameters(0, 2)
|
||||
swapParameters(1, 2)
|
||||
}
|
||||
|
||||
fun testAddDataClassParameter() = doTest {
|
||||
addParameter(KotlinParameterInfo(originalBaseFunctionDescriptor,
|
||||
-1,
|
||||
"c",
|
||||
KotlinTypeInfo(false, BUILT_INS.intType),
|
||||
null,
|
||||
KtPsiFactory(project).createExpression("3"),
|
||||
KotlinValVar.Val),
|
||||
1)
|
||||
}
|
||||
|
||||
fun testRemoveDataClassParameter() = doTest { removeParameter(1) }
|
||||
|
||||
fun testRemoveAllOriginalDataClassParameters() = doTest {
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
|
||||
swapParameters(1, 2)
|
||||
setNewParameter(0,
|
||||
KotlinParameterInfo(originalBaseFunctionDescriptor,
|
||||
-1,
|
||||
"d",
|
||||
KotlinTypeInfo(false, BUILT_INS.intType),
|
||||
null,
|
||||
psiFactory.createExpression("4"),
|
||||
KotlinValVar.Val))
|
||||
|
||||
setNewParameter(2,
|
||||
KotlinParameterInfo(originalBaseFunctionDescriptor,
|
||||
-1,
|
||||
"e",
|
||||
KotlinTypeInfo(false, BUILT_INS.intType),
|
||||
null,
|
||||
psiFactory.createExpression("5"),
|
||||
KotlinValVar.Val))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user