Introduce Variable: Use expression unifier to find duplicates

This commit is contained in:
Alexey Sedunov
2014-07-31 18:37:02 +04:00
parent f1c18d0e3f
commit 855ed81c11
6 changed files with 54 additions and 79 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.refactoring.introduce.introduceVariable;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
@@ -31,6 +30,7 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -42,7 +42,6 @@ import org.jetbrains.jet.lang.resolve.BindingTraceContext;
import org.jetbrains.jet.lang.resolve.ObservableBindingTrace;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
@@ -52,20 +51,25 @@ import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils;
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester;
import org.jetbrains.jet.plugin.refactoring.JetNameValidatorImpl;
import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle;
import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil;
import org.jetbrains.jet.plugin.refactoring.introduce.KotlinIntroduceHandlerBase;
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange;
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiUnifier;
import org.jetbrains.jet.plugin.util.psi.patternMatching.PatternMatchingPackage;
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnifierParameter;
import org.jetbrains.jet.plugin.util.psiModificationUtil.PsiModificationUtilPackage;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase {
@@ -431,84 +435,21 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase {
return anchor;
}
private static ArrayList<JetExpression> findOccurrences(PsiElement occurrenceContainer, @NotNull JetExpression expression) {
if (expression instanceof JetParenthesizedExpression) {
JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression)expression;
JetExpression innerExpression = parenthesizedExpression.getExpression();
if (innerExpression != null) {
expression = innerExpression;
}
}
final JetExpression actualExpression = expression;
final ArrayList<JetExpression> result = new ArrayList<JetExpression>();
final BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement(expression);
JetVisitorVoid visitor = new JetVisitorVoid() {
@Override
public void visitJetElement(@NotNull JetElement element) {
element.acceptChildren(this);
super.visitJetElement(element);
}
@Override
public void visitExpression(@NotNull JetExpression expression) {
if (PsiEquivalenceUtil.areElementsEquivalent(expression, actualExpression, null, new Comparator<PsiElement>() {
private boolean compareCalleesAndReceivers(@NotNull ResolvedCall<?> rc1, @NotNull ResolvedCall<?> rc2) {
if (rc1.getResultingDescriptor() != rc2.getResultingDescriptor() ||
rc1.getExplicitReceiverKind() != rc2.getExplicitReceiverKind()) return false;
switch (rc1.getExplicitReceiverKind()) {
case NO_EXPLICIT_RECEIVER:
return rc1.getReceiverArgument() == rc2.getReceiverArgument()
&& rc1.getThisObject() == rc2.getThisObject();
case RECEIVER_ARGUMENT:
return rc1.getThisObject() == rc2.getThisObject();
case THIS_OBJECT:
return rc1.getReceiverArgument() == rc2.getReceiverArgument();
default:
return true;
}
}
private static List<JetExpression> findOccurrences(PsiElement occurrenceContainer, @NotNull JetExpression originalExpression) {
return KotlinPackage.map(
PatternMatchingPackage.toRange(originalExpression).match(occurrenceContainer, JetPsiUnifier.DEFAULT),
new Function1<JetPsiRange.Match, JetExpression>() {
@Override
public int compare(@NotNull PsiElement element1, @NotNull PsiElement element2) {
if (element1.getNode().getElementType() == JetTokens.IDENTIFIER &&
element2.getNode().getElementType() == JetTokens.IDENTIFIER) {
if (element1.getParent() instanceof JetSimpleNameExpression &&
element2.getParent() instanceof JetSimpleNameExpression) {
JetSimpleNameExpression expr1 = (JetSimpleNameExpression)element1.getParent();
JetSimpleNameExpression expr2 = (JetSimpleNameExpression)element2.getParent();
public JetExpression invoke(JetPsiRange.Match match) {
PsiElement candidate = ((JetPsiRange.ListRange) match.getRange()).getStartElement();
if (candidate instanceof JetExpression) return (JetExpression) candidate;
if (candidate instanceof JetStringTemplateEntryWithExpression)
return ((JetStringTemplateEntryWithExpression) candidate).getExpression();
ResolvedCall<?> rc1 = getResolvedCall(expr1, bindingContext);
ResolvedCall<?> rc2 = getResolvedCall(expr2, bindingContext);
return (rc1 != null && rc2 != null) && compareCalleesAndReceivers(rc1, rc2) ? 0 : 1;
}
}
if (!element1.textMatches(element2)) {
return 1;
}
else {
return 0;
}
}
}, null, false)) {
PsiElement parent = expression.getParent();
if (parent instanceof JetParenthesizedExpression) {
result.add((JetParenthesizedExpression)parent);
}
else {
result.add(expression);
throw new AssertionError("Unexpected candidate element: " + candidate.getText());
}
}
else {
super.visitExpression(expression);
}
}
};
occurrenceContainer.accept(visitor);
return result;
);
}
@Nullable
@@ -0,0 +1,4 @@
fun a(x: Int) {
val a = <selection>x + 1</selection>
"it's a number ${x + 1}"
}
@@ -0,0 +1,5 @@
fun a(x: Int) {
val i = x + 1
val a = i
"it's a number ${i}"
}
@@ -0,0 +1,7 @@
fun a() {
val x = <selection>Foo()</selection>
println(Foo())
println(Foo)
println(Foo(1))
println(1.Foo())
}
@@ -0,0 +1,8 @@
fun a() {
val foo = Foo()
val x = foo
println(foo)
println(Foo)
println(Foo(1))
println(1.Foo())
}
@@ -173,6 +173,11 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doIntroduceVariableTest(fileName);
}
@TestMetadata("OccurrencesInStringTemplate.kt")
public void testOccurrencesInStringTemplate() throws Exception {
doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/OccurrencesInStringTemplate.kt");
}
@TestMetadata("OneExplicitReceiver.kt")
public void testOneExplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/OneExplicitReceiver.kt");
@@ -209,6 +214,11 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doIntroduceVariableTest(fileName);
}
@TestMetadata("UnresolvedOccurrences.kt")
public void testUnresolvedOccurrences() throws Exception {
doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/UnresolvedOccurrences.kt");
}
@TestMetadata("WhenAddBlock.kt")
public void testWhenAddBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/WhenAddBlock.kt");