diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt index 26916e5d3f0..b182cf45caa 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java index be8f0564ec4..2386979e0e1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -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 refUsages, MultiMap 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" + ); } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt index c43224b3226..a0db09323bd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt @@ -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(element: T) : JetUsageInfo(element) { +public abstract class JetExplicitReferenceUsage(element: T) : JetUsageInfo(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( diff --git a/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt b/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt new file mode 100644 index 00000000000..33f5eacbbc0 --- /dev/null +++ b/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt @@ -0,0 +1,7 @@ +interface T { + val foo: Int +} + +val f = fun(t: T): Int { + return t.foo +} \ No newline at end of file diff --git a/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt.after b/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt.after new file mode 100644 index 00000000000..51598b1c161 --- /dev/null +++ b/idea/testData/intentions/convertParameterToReceiver/functionExpression.kt.after @@ -0,0 +1,7 @@ +interface T { + val foo: Int +} + +val f = fun T.(): Int { + return foo +} \ No newline at end of file diff --git a/idea/testData/intentions/convertParameterToReceiver/functionExpressionWithThisConflict.kt b/idea/testData/intentions/convertParameterToReceiver/functionExpressionWithThisConflict.kt new file mode 100644 index 00000000000..64ab49984d2 --- /dev/null +++ b/idea/testData/intentions/convertParameterToReceiver/functionExpressionWithThisConflict.kt @@ -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(t: T) { + object { + fun f(): Int { + return t.foo + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt b/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt new file mode 100644 index 00000000000..608175a58ad --- /dev/null +++ b/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt @@ -0,0 +1,7 @@ +interface T { + val foo: Int +} + +val f = fun T.(): Int { + return foo +} \ No newline at end of file diff --git a/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt.after b/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt.after new file mode 100644 index 00000000000..adff11d5262 --- /dev/null +++ b/idea/testData/intentions/convertReceiverToParameter/functionExpression.kt.after @@ -0,0 +1,7 @@ +interface T { + val foo: Int +} + +val f = fun(t: T): Int { + return t.foo +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 1adde2d43e8..45f693a54f5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -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");