Change Signature: Support receiver <-> parameter conversion for function expressions
#KT-9309 Fixed
This commit is contained in:
@@ -65,7 +65,7 @@ fun JetCallableDeclaration.setReceiverTypeReference(typeRef: JetTypeReference?):
|
|||||||
oldTypeRef.replace(typeRef) as JetTypeReference
|
oldTypeRef.replace(typeRef) as JetTypeReference
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val anchor = getNameIdentifier()
|
val anchor = getNameIdentifier() ?: valueParameterList
|
||||||
val newTypeRef = addBefore(typeRef, anchor) as JetTypeReference
|
val newTypeRef = addBefore(typeRef, anchor) as JetTypeReference
|
||||||
addAfter(JetPsiFactory(getProject()).createDot(), newTypeRef)
|
addAfter(JetPsiFactory(getProject()).createDot(), newTypeRef)
|
||||||
newTypeRef
|
newTypeRef
|
||||||
|
|||||||
+21
-8
@@ -75,6 +75,7 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
|
||||||
import org.jetbrains.kotlin.types.JetType;
|
import org.jetbrains.kotlin.types.JetType;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -671,7 +672,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
|||||||
if (function instanceof JetCallableDeclaration && newReceiverInfo != originalReceiverInfo) {
|
if (function instanceof JetCallableDeclaration && newReceiverInfo != originalReceiverInfo) {
|
||||||
findReceiverIntroducingConflicts(result, function, newReceiverInfo);
|
findReceiverIntroducingConflicts(result, function, newReceiverInfo);
|
||||||
findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo);
|
findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo);
|
||||||
findThisLabelConflicts((JetChangeInfo) info, refUsages, result, changeInfo, function);
|
findThisLabelConflicts(refUsages, result, changeInfo, (JetCallableDeclaration) function);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UsageInfo usageInfo : usageInfos) {
|
for (UsageInfo usageInfo : usageInfos) {
|
||||||
@@ -716,18 +717,17 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void findThisLabelConflicts(
|
private static void findThisLabelConflicts(
|
||||||
JetChangeInfo info,
|
|
||||||
Ref<UsageInfo[]> refUsages,
|
Ref<UsageInfo[]> refUsages,
|
||||||
MultiMap<PsiElement, String> result,
|
MultiMap<PsiElement, String> result,
|
||||||
JetChangeInfo changeInfo,
|
JetChangeInfo changeInfo,
|
||||||
PsiElement function
|
JetCallableDeclaration callable
|
||||||
) {
|
) {
|
||||||
JetPsiFactory psiFactory = new JetPsiFactory(function.getProject());
|
JetPsiFactory psiFactory = new JetPsiFactory(callable.getProject());
|
||||||
for (UsageInfo usageInfo : refUsages.get()) {
|
for (UsageInfo usageInfo : refUsages.get()) {
|
||||||
if (!(usageInfo instanceof JetParameterUsage)) continue;
|
if (!(usageInfo instanceof JetParameterUsage)) continue;
|
||||||
|
|
||||||
String newExprText = ((JetParameterUsage) usageInfo).getReplacementText(changeInfo);
|
String newExprText = ((JetParameterUsage) usageInfo).getReplacementText(changeInfo);
|
||||||
if (!newExprText.startsWith("this@")) continue;
|
if (!newExprText.startsWith("this")) continue;
|
||||||
|
|
||||||
if (usageInfo.getElement() instanceof KDocName) continue; // TODO support converting parameter to receiver in KDoc
|
if (usageInfo.getElement() instanceof KDocName) continue; // TODO support converting parameter to receiver in KDoc
|
||||||
|
|
||||||
@@ -737,12 +737,11 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
|||||||
if (scope == null) continue;
|
if (scope == null) continue;
|
||||||
|
|
||||||
JetThisExpression newExpr = (JetThisExpression) psiFactory.createExpression(newExprText);
|
JetThisExpression newExpr = (JetThisExpression) psiFactory.createExpression(newExprText);
|
||||||
JetSimpleNameExpression labelExpr = newExpr.getTargetLabel();
|
|
||||||
if (labelExpr == null) continue;
|
|
||||||
|
|
||||||
BindingContext newContext = AnalysisPackage.analyzeInContext(newExpr, scope, originalExpr);
|
BindingContext newContext = AnalysisPackage.analyzeInContext(newExpr, scope, originalExpr);
|
||||||
|
|
||||||
if (newContext.get(BindingContext.AMBIGUOUS_LABEL_TARGET, labelExpr) != null) {
|
JetSimpleNameExpression labelExpr = newExpr.getTargetLabel();
|
||||||
|
if (labelExpr != null && newContext.get(BindingContext.AMBIGUOUS_LABEL_TARGET, labelExpr) != null) {
|
||||||
result.putValue(
|
result.putValue(
|
||||||
originalExpr,
|
originalExpr,
|
||||||
"Parameter reference can't be safely replaced with " +
|
"Parameter reference can't be safely replaced with " +
|
||||||
@@ -751,6 +750,20 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
|||||||
labelExpr.getText() +
|
labelExpr.getText() +
|
||||||
" is ambiguous in this context"
|
" is ambiguous in this context"
|
||||||
);
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclarationDescriptor thisTarget = newContext.get(BindingContext.REFERENCE_TARGET, newExpr.getInstanceReference());
|
||||||
|
PsiElement thisTargetPsi = thisTarget instanceof DeclarationDescriptorWithSource
|
||||||
|
? KotlinSourceElementKt.getPsi(((DeclarationDescriptorWithSource) thisTarget).getSource())
|
||||||
|
: null;
|
||||||
|
if (thisTargetPsi != null && PsiTreeUtil.isAncestor(callable, thisTargetPsi, true)) {
|
||||||
|
result.putValue(
|
||||||
|
originalExpr,
|
||||||
|
"Parameter reference can't be safely replaced with " +
|
||||||
|
newExprText +
|
||||||
|
" since target function can't be referenced in this context"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-5
@@ -23,13 +23,14 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||||
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.psi.JetQualifiedExpression
|
import org.jetbrains.kotlin.psi.JetQualifiedExpression
|
||||||
import org.jetbrains.kotlin.psi.JetElement
|
import org.jetbrains.kotlin.psi.JetElement
|
||||||
import org.jetbrains.kotlin.psi.JetThisExpression
|
import org.jetbrains.kotlin.psi.JetThisExpression
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
|
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
|
||||||
|
|
||||||
// Explicit reference to function parameter or outer this
|
// Explicit reference to function parameter or outer this
|
||||||
public abstract class JetExplicitReferenceUsage<T: JetElement>(element: T) : JetUsageInfo<T>(element) {
|
public abstract class JetExplicitReferenceUsage<T : JetElement>(element: T) : JetUsageInfo<T>(element) {
|
||||||
abstract fun getReplacementText(changeInfo: JetChangeInfo): String
|
abstract fun getReplacementText(changeInfo: JetChangeInfo): String
|
||||||
|
|
||||||
protected open fun processReplacedElement(element: JetElement) {
|
protected open fun processReplacedElement(element: JetElement) {
|
||||||
@@ -55,10 +56,14 @@ public class JetParameterUsage(
|
|||||||
elementToShorten.addToShorteningWaitSet(Options(removeThis = true, removeThisLabels = true))
|
elementToShorten.addToShorteningWaitSet(Options(removeThis = true, removeThisLabels = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getReplacementText(changeInfo: JetChangeInfo): String =
|
override fun getReplacementText(changeInfo: JetChangeInfo): String {
|
||||||
if (changeInfo.receiverParameterInfo != parameterInfo) {
|
if (changeInfo.receiverParameterInfo != parameterInfo) return parameterInfo.getInheritedName(containingCallable)
|
||||||
parameterInfo.getInheritedName(containingCallable)
|
|
||||||
} else "this@${changeInfo.getNewName()}"
|
val newName = changeInfo.getNewName()
|
||||||
|
if (KotlinNameSuggester.isIdentifier(newName)) return "this@$newName"
|
||||||
|
|
||||||
|
return "this"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JetNonQualifiedOuterThisUsage(
|
public class JetNonQualifiedOuterThisUsage(
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface T {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val f = fun(<caret>t: T): Int {
|
||||||
|
return t.foo
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
interface T {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val f = fun T.(): Int {
|
||||||
|
return foo
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// SHOULD_FAIL_WITH: Parameter reference can't be safely replaced with this since target function can't be referenced in this context
|
||||||
|
interface T {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val f = fun(<caret>t: T) {
|
||||||
|
object {
|
||||||
|
fun f(): Int {
|
||||||
|
return t.foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface T {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val f = fun <caret>T.(): Int {
|
||||||
|
return foo
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
interface T {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
val f = fun(t: T): Int {
|
||||||
|
return t.foo
|
||||||
|
}
|
||||||
@@ -3565,6 +3565,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionExpression.kt")
|
||||||
|
public void testFunctionExpression() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/functionExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionExpressionWithThisConflict.kt")
|
||||||
|
public void testFunctionExpressionWithThisConflict() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/functionExpressionWithThisConflict.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("lambdaParameter.kt")
|
@TestMetadata("lambdaParameter.kt")
|
||||||
public void testLambdaParameter() throws Exception {
|
public void testLambdaParameter() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/lambdaParameter.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/lambdaParameter.kt");
|
||||||
@@ -3727,6 +3739,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertReceiverToParameter"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertReceiverToParameter"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionExpression.kt")
|
||||||
|
public void testFunctionExpression() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertReceiverToParameter/functionExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("genericReceiver.kt")
|
@TestMetadata("genericReceiver.kt")
|
||||||
public void testGenericReceiver() throws Exception {
|
public void testGenericReceiver() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertReceiverToParameter/genericReceiver.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertReceiverToParameter/genericReceiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user