Java: Prevent introducing parameter that contains subexpression replaced by another parameter (IDEA-180801)

This commit is contained in:
Pavel Dolgov
2017-10-26 18:51:31 +03:00
parent 4b3e26fb2f
commit 550f61fa53
5 changed files with 74 additions and 3 deletions

View File

@@ -18,6 +18,7 @@ import com.intellij.refactoring.util.VariableData;
import com.intellij.refactoring.util.duplicates.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -59,9 +60,12 @@ public class JavaDuplicatesExtractMethodProcessor extends ExtractMethodProcessor
variableDatum.add(newData);
}
}
Set<PsiVariable> parameterVariables = ContainerUtil.map2Set(variableDatum, data -> data.variable);
List<VariableData> inputVariables = getInputVariables().getInputVariables();
for (int i = variableDatum.size(); i < inputVariables.size(); i++) {
variableDatum.add(inputVariables.get(i));
for (VariableData data : inputVariables) {
if (!parameterVariables.contains(data.variable)) {
variableDatum.add(data);
}
}
myVariableDatum = variableDatum.toArray(new VariableData[0]);
}

View File

@@ -145,6 +145,8 @@ public class ParametrizedDuplicates {
if (myElements.length == 0) {
return false;
}
matches = filterNestedSubexpressions(matches);
myUsagesList = new ArrayList<>();
Map<PsiExpression, ClusterOfUsages> usagesMap = new THashMap<>();
Set<Match> badMatches = new THashSet<>();
@@ -185,6 +187,38 @@ public class ParametrizedDuplicates {
return true;
}
private static List<Match> filterNestedSubexpressions(List<Match> matches) {
Map<PsiExpression, Set<Match>> patternUsages = new THashMap<>();
for (Match match : matches) {
for (ExtractedParameter parameter : match.getExtractedParameters()) {
for (PsiExpression patternUsage : parameter.myPatternUsages) {
patternUsages.computeIfAbsent(patternUsage, k -> new THashSet<>()).add(match);
}
}
}
Set<Match> badMatches = new THashSet<>();
for (Map.Entry<PsiExpression, Set<Match>> entry : patternUsages.entrySet()) {
PsiExpression patternUsage = entry.getKey();
Set<Match> patternMatches = entry.getValue();
for (PsiExpression maybeNestedUsage : patternUsages.keySet()) {
if (patternUsage == maybeNestedUsage) {
continue;
}
if (PsiTreeUtil.isAncestor(patternUsage, maybeNestedUsage, true)) {
badMatches.addAll(patternMatches);
break;
}
}
}
if (!badMatches.isEmpty()) {
matches = new ArrayList<>(matches);
matches.removeAll(badMatches);
}
return matches;
}
@Nullable
private static List<ClusterOfUsages> getUsagesInMatch(@NotNull Map<PsiExpression, ClusterOfUsages> usagesMap, @NotNull Match match) {
List<ClusterOfUsages> result = new ArrayList<>();
@@ -217,7 +251,6 @@ public class ParametrizedDuplicates {
}
parametrizedProcessor.applyFrom(originalProcessor, variablesMapping);
parametrizedProcessor.doExtract();
parametrizedProcessor.setDataFromInputVariables();
myParametrizedMethod = parametrizedProcessor.getExtractedMethod();
myParametrizedCall = parametrizedProcessor.getMethodCall();
myVariableDatum = unmapVariableData(parametrizedProcessor.myVariableDatum, variablesMapping);

View File

@@ -0,0 +1,13 @@
class C {
public void foo(C c, long d, int i, String s) {
<selection>b(s, 1).m(d, i())</selection>;
b(s, i).m(d, k(d));
c.m(d, i);
}
private C b(String s, int i) { return new C(); }
void m(long d, int i) { }
private int i() { return 0; }
private int k(long d) { return 0; }
}

View File

@@ -0,0 +1,17 @@
class C {
public void foo(C c, long d, int i, String s) {
newMethod(d, s, 1, i());
newMethod(d, s, i, k(d));
c.m(d, i);
}
private void newMethod(long d, String s, int i, int i2) {
b(s, i).m(d, i2);
}
private C b(String s, int i) { return new C(); }
void m(long d, int i) { }
private int i() { return 0; }
private int k(long d) { return 0; }
}

View File

@@ -868,6 +868,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doDuplicatesTest();
}
public void testParametrizedDuplicateNestedSubexpression() throws Exception {
doDuplicatesTest();
}
public void testSuggestChangeSignatureWithChangedParameterName() throws Exception {
configureByFile(BASE_PATH + getTestName(false) + ".java");
boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, false, "p");