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,
|
||||
true
|
||||
);
|
||||
|
||||
return findWhatPanel;
|
||||
}
|
||||
|
||||
|
||||
+50
-4
@@ -44,6 +44,7 @@ import kotlin.KotlinPackage;
|
||||
import kotlin.Unit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalyzerPackage;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.idea.JetFileType;
|
||||
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.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceContext;
|
||||
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.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
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.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
|
||||
@Override
|
||||
@@ -424,11 +426,55 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
|
||||
if (function instanceof JetNamedFunction && newReceiverInfo != originalReceiverInfo) {
|
||||
findReceiverIntroducingConflicts(result, function, newReceiverInfo);
|
||||
findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo);
|
||||
findThisLabelConflicts((JetChangeInfo) info, refUsages, result, changeInfo, function);
|
||||
}
|
||||
|
||||
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(
|
||||
UsageInfo[] usages,
|
||||
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
|
||||
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) {
|
||||
|
||||
@@ -45,17 +45,18 @@ public abstract class JetExplicitReferenceUsage<T: JetElement>(element: T) : Jet
|
||||
public class JetParameterUsage(
|
||||
element: JetSimpleNameExpression,
|
||||
private val parameterInfo: JetParameterInfo,
|
||||
private val containingFunction: JetFunctionDefinitionUsage<*>
|
||||
val containingFunction: JetFunctionDefinitionUsage<*>
|
||||
) : JetExplicitReferenceUsage<JetSimpleNameExpression>(element) {
|
||||
override fun processReplacedElement(element: JetElement) {
|
||||
val qualifiedExpression = element.getParent() as? JetQualifiedExpression
|
||||
if (qualifiedExpression?.getReceiverExpression() == element) {
|
||||
qualifiedExpression!!.addToShorteningWaitSet(Options(removeThis = true))
|
||||
}
|
||||
val elementToShorten = if (qualifiedExpression?.getReceiverExpression() == element) qualifiedExpression!! else element
|
||||
elementToShorten.addToShorteningWaitSet(Options(removeThis = true, removeThisLabels = true))
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testMemberFun() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertParameterToReceiver/memberFun.kt");
|
||||
|
||||
Reference in New Issue
Block a user