Convert Parameter to Receiver: Replace parameter references with labeled this-expressions (before shortening them). Report conflict when this-label is ambiguous
#KT-7681 Fixed
This commit is contained in:
@@ -136,7 +136,6 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
|
|||||||
findWhatPanel,
|
findWhatPanel,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
return findWhatPanel;
|
return findWhatPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-4
@@ -44,6 +44,7 @@ import kotlin.KotlinPackage;
|
|||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.analyzer.AnalyzerPackage;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.idea.JetFileType;
|
import org.jetbrains.kotlin.idea.JetFileType;
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||||
@@ -59,19 +60,20 @@ import org.jetbrains.kotlin.psi.*;
|
|||||||
import org.jetbrains.kotlin.psi.typeRefHelpers.TypeRefHelpersPackage;
|
import org.jetbrains.kotlin.psi.typeRefHelpers.TypeRefHelpersPackage;
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTraceContext;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
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.types.JetType;
|
import org.jetbrains.kotlin.types.JetType;
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
|
public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
|
||||||
@Override
|
@Override
|
||||||
@@ -424,11 +426,55 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
|||||||
if (function instanceof JetNamedFunction && newReceiverInfo != originalReceiverInfo) {
|
if (function instanceof JetNamedFunction && 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void findThisLabelConflicts(
|
||||||
|
JetChangeInfo info,
|
||||||
|
Ref<UsageInfo[]> refUsages,
|
||||||
|
MultiMap<PsiElement, String> result,
|
||||||
|
JetChangeInfo changeInfo,
|
||||||
|
PsiElement function
|
||||||
|
) {
|
||||||
|
JetPsiFactory psiFactory = new JetPsiFactory(function.getProject());
|
||||||
|
for (UsageInfo usageInfo : refUsages.get()) {
|
||||||
|
if (!(usageInfo instanceof JetParameterUsage)) continue;
|
||||||
|
|
||||||
|
String newExprText = ((JetParameterUsage) usageInfo).getReplacementText(changeInfo);
|
||||||
|
if (!newExprText.startsWith("this@")) continue;
|
||||||
|
|
||||||
|
JetExpression originalExpr = (JetExpression) usageInfo.getElement();
|
||||||
|
JetScope scope = ResolvePackage.analyze(originalExpr, BodyResolveMode.FULL)
|
||||||
|
.get(BindingContext.RESOLUTION_SCOPE, originalExpr);
|
||||||
|
if (scope == null) continue;
|
||||||
|
|
||||||
|
JetThisExpression newExpr = (JetThisExpression) psiFactory.createExpression(newExprText);
|
||||||
|
JetSimpleNameExpression labelExpr = newExpr.getTargetLabel();
|
||||||
|
if (labelExpr == null) continue;
|
||||||
|
|
||||||
|
BindingContext newContext =
|
||||||
|
AnalyzerPackage.analyzeInContext(newExpr,
|
||||||
|
scope,
|
||||||
|
new BindingTraceContext(),
|
||||||
|
DataFlowInfo.EMPTY,
|
||||||
|
TypeUtils.NO_EXPECTED_TYPE,
|
||||||
|
DescriptorUtils.getContainingModule(scope.getContainingDeclaration()));
|
||||||
|
if (newContext.get(BindingContext.AMBIGUOUS_LABEL_TARGET, labelExpr) != null) {
|
||||||
|
result.putValue(
|
||||||
|
originalExpr,
|
||||||
|
"Parameter reference can't be safely replaced with " +
|
||||||
|
newExprText +
|
||||||
|
" since " +
|
||||||
|
labelExpr.getText() +
|
||||||
|
" is ambiguous in this context"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void findInternalExplicitReceiverConflicts(
|
private static void findInternalExplicitReceiverConflicts(
|
||||||
UsageInfo[] usages,
|
UsageInfo[] usages,
|
||||||
MultiMap<PsiElement, String> result,
|
MultiMap<PsiElement, String> result,
|
||||||
|
|||||||
+7
-6
@@ -29,7 +29,7 @@ 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) {
|
||||||
protected abstract fun getReplacementText(changeInfo: JetChangeInfo): String
|
abstract fun getReplacementText(changeInfo: JetChangeInfo): String
|
||||||
|
|
||||||
protected open fun processReplacedElement(element: JetElement) {
|
protected open fun processReplacedElement(element: JetElement) {
|
||||||
|
|
||||||
@@ -45,17 +45,18 @@ public abstract class JetExplicitReferenceUsage<T: JetElement>(element: T) : Jet
|
|||||||
public class JetParameterUsage(
|
public class JetParameterUsage(
|
||||||
element: JetSimpleNameExpression,
|
element: JetSimpleNameExpression,
|
||||||
private val parameterInfo: JetParameterInfo,
|
private val parameterInfo: JetParameterInfo,
|
||||||
private val containingFunction: JetFunctionDefinitionUsage<*>
|
val containingFunction: JetFunctionDefinitionUsage<*>
|
||||||
) : JetExplicitReferenceUsage<JetSimpleNameExpression>(element) {
|
) : JetExplicitReferenceUsage<JetSimpleNameExpression>(element) {
|
||||||
override fun processReplacedElement(element: JetElement) {
|
override fun processReplacedElement(element: JetElement) {
|
||||||
val qualifiedExpression = element.getParent() as? JetQualifiedExpression
|
val qualifiedExpression = element.getParent() as? JetQualifiedExpression
|
||||||
if (qualifiedExpression?.getReceiverExpression() == element) {
|
val elementToShorten = if (qualifiedExpression?.getReceiverExpression() == element) qualifiedExpression!! else element
|
||||||
qualifiedExpression!!.addToShorteningWaitSet(Options(removeThis = true))
|
elementToShorten.addToShorteningWaitSet(Options(removeThis = true, removeThisLabels = true))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getReplacementText(changeInfo: JetChangeInfo): String =
|
override fun getReplacementText(changeInfo: JetChangeInfo): String =
|
||||||
if (changeInfo.receiverParameterInfo != parameterInfo) parameterInfo.getInheritedName(containingFunction) else "this"
|
if (changeInfo.receiverParameterInfo != parameterInfo) {
|
||||||
|
parameterInfo.getInheritedName(containingFunction)
|
||||||
|
} else "this@${containingFunction.getOriginalFunctionDescriptor().getName().asString()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JetNonQualifiedOuterThisUsage(
|
public class JetNonQualifiedOuterThisUsage(
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(<caret>bar: Int) {
|
||||||
|
object {
|
||||||
|
fun baz() {
|
||||||
|
bar + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun Int.foo() {
|
||||||
|
object {
|
||||||
|
fun baz() {
|
||||||
|
this@foo + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// SHOULD_FAIL_WITH: Parameter reference can't be safely replaced with this@foo since @foo is ambiguous in this context
|
||||||
|
fun foo(<caret>bar: Int) {
|
||||||
|
object {
|
||||||
|
fun foo() {
|
||||||
|
bar + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3226,6 +3226,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localObject.kt")
|
||||||
|
public void testLocalObject() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/localObject.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localObjectWithFunctionNameConflict.kt")
|
||||||
|
public void testLocalObjectWithFunctionNameConflict() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/localObjectWithFunctionNameConflict.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("memberFun.kt")
|
@TestMetadata("memberFun.kt")
|
||||||
public void testMemberFun() throws Exception {
|
public void testMemberFun() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/memberFun.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/memberFun.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user