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
|
||||
}
|
||||
else {
|
||||
val anchor = getNameIdentifier()
|
||||
val anchor = getNameIdentifier() ?: valueParameterList
|
||||
val newTypeRef = addBefore(typeRef, anchor) as JetTypeReference
|
||||
addAfter(JetPsiFactory(getProject()).createDot(), 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.ThisReceiver;
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
|
||||
import java.util.*;
|
||||
@@ -671,7 +672,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
||||
if (function instanceof JetCallableDeclaration && newReceiverInfo != originalReceiverInfo) {
|
||||
findReceiverIntroducingConflicts(result, function, newReceiverInfo);
|
||||
findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo);
|
||||
findThisLabelConflicts((JetChangeInfo) info, refUsages, result, changeInfo, function);
|
||||
findThisLabelConflicts(refUsages, result, changeInfo, (JetCallableDeclaration) function);
|
||||
}
|
||||
|
||||
for (UsageInfo usageInfo : usageInfos) {
|
||||
@@ -716,18 +717,17 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
||||
}
|
||||
|
||||
private static void findThisLabelConflicts(
|
||||
JetChangeInfo info,
|
||||
Ref<UsageInfo[]> refUsages,
|
||||
MultiMap<PsiElement, String> result,
|
||||
JetChangeInfo changeInfo,
|
||||
PsiElement function
|
||||
JetCallableDeclaration callable
|
||||
) {
|
||||
JetPsiFactory psiFactory = new JetPsiFactory(function.getProject());
|
||||
JetPsiFactory psiFactory = new JetPsiFactory(callable.getProject());
|
||||
for (UsageInfo usageInfo : refUsages.get()) {
|
||||
if (!(usageInfo instanceof JetParameterUsage)) continue;
|
||||
|
||||
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
|
||||
|
||||
@@ -737,12 +737,11 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
||||
if (scope == null) continue;
|
||||
|
||||
JetThisExpression newExpr = (JetThisExpression) psiFactory.createExpression(newExprText);
|
||||
JetSimpleNameExpression labelExpr = newExpr.getTargetLabel();
|
||||
if (labelExpr == null) continue;
|
||||
|
||||
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(
|
||||
originalExpr,
|
||||
"Parameter reference can't be safely replaced with " +
|
||||
@@ -751,6 +750,20 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
||||
labelExpr.getText() +
|
||||
" 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.descriptors.DeclarationDescriptor
|
||||
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.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetThisExpression
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
|
||||
|
||||
// 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
|
||||
|
||||
protected open fun processReplacedElement(element: JetElement) {
|
||||
@@ -55,10 +56,14 @@ public class JetParameterUsage(
|
||||
elementToShorten.addToShorteningWaitSet(Options(removeThis = true, removeThisLabels = true))
|
||||
}
|
||||
|
||||
override fun getReplacementText(changeInfo: JetChangeInfo): String =
|
||||
if (changeInfo.receiverParameterInfo != parameterInfo) {
|
||||
parameterInfo.getInheritedName(containingCallable)
|
||||
} else "this@${changeInfo.getNewName()}"
|
||||
override fun getReplacementText(changeInfo: JetChangeInfo): String {
|
||||
if (changeInfo.receiverParameterInfo != parameterInfo) return parameterInfo.getInheritedName(containingCallable)
|
||||
|
||||
val newName = changeInfo.getNewName()
|
||||
if (KotlinNameSuggester.isIdentifier(newName)) return "this@$newName"
|
||||
|
||||
return "this"
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testLambdaParameter() throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
@TestMetadata("functionExpression.kt")
|
||||
public void testFunctionExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertReceiverToParameter/functionExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericReceiver.kt")
|
||||
public void testGenericReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertReceiverToParameter/genericReceiver.kt");
|
||||
|
||||
Reference in New Issue
Block a user