mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Recognize multiple parametrized duplicates when extracting a method (IDEA-188243)
This commit is contained in:
+6
-1
@@ -284,6 +284,10 @@ public class InputVariables {
|
||||
return inputVariables;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public InputVariables copyWithoutFolding() {
|
||||
return new InputVariables(myInitialParameters, myProject, myScope, false);
|
||||
}
|
||||
|
||||
public void appendCallArguments(VariableData data, StringBuilder buffer) {
|
||||
if (myFoldingAvailable) {
|
||||
@@ -309,8 +313,9 @@ public class InputVariables {
|
||||
}
|
||||
|
||||
public void annotateWithParameter(PsiJavaCodeReferenceElement reference) {
|
||||
if (myInputVariables.isEmpty()) return;
|
||||
final PsiElement element = reference.resolve();
|
||||
for (VariableData data : myInputVariables) {
|
||||
final PsiElement element = reference.resolve();
|
||||
if (data.variable.equals(element)) {
|
||||
PsiType type = data.variable.getType();
|
||||
final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(reference, PsiMethodCallExpression.class);
|
||||
|
||||
+1
-1
@@ -353,7 +353,7 @@ public class ParametersFolder {
|
||||
if (psiExpression != null) {
|
||||
final PsiExpression expression = findEquivalent(psiExpression, element);
|
||||
if (expression != null) {
|
||||
expression.putUserData(DuplicatesFinder.PARAMETER, new DuplicatesFinder.Parameter(data.variable, expression.getType()));
|
||||
expression.putUserData(DuplicatesFinder.PARAMETER, new DuplicatesFinder.Parameter(data.variable, expression.getType(), true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-10
@@ -48,7 +48,7 @@ public class DuplicatesFinder {
|
||||
private final List<PsiElement> myPatternAsList;
|
||||
private boolean myMultipleExitPoints;
|
||||
@Nullable private final ReturnValue myReturnValue;
|
||||
private final boolean myWithExtractedParameters;
|
||||
private final MatchType myMatchType;
|
||||
private final Set<PsiVariable> myEffectivelyLocal;
|
||||
private ComplexityHolder myPatternComplexityHolder;
|
||||
private ComplexityHolder myCandidateComplexityHolder;
|
||||
@@ -57,7 +57,7 @@ public class DuplicatesFinder {
|
||||
InputVariables parameters,
|
||||
@Nullable ReturnValue returnValue,
|
||||
@NotNull List<? extends PsiVariable> outputParameters,
|
||||
boolean withExtractedParameters,
|
||||
@NotNull MatchType matchType,
|
||||
@Nullable Set<PsiVariable> effectivelyLocal) {
|
||||
myReturnValue = returnValue;
|
||||
LOG.assertTrue(pattern.length > 0);
|
||||
@@ -65,7 +65,7 @@ public class DuplicatesFinder {
|
||||
myPatternAsList = Arrays.asList(myPattern);
|
||||
myParameters = parameters;
|
||||
myOutputParameters = outputParameters;
|
||||
myWithExtractedParameters = withExtractedParameters;
|
||||
myMatchType = matchType;
|
||||
myEffectivelyLocal = effectivelyLocal != null ? effectivelyLocal : Collections.emptySet();
|
||||
|
||||
final PsiElement codeFragment = ControlFlowUtil.findCodeFragment(pattern[0]);
|
||||
@@ -101,7 +101,7 @@ public class DuplicatesFinder {
|
||||
InputVariables parameters,
|
||||
@Nullable ReturnValue returnValue,
|
||||
@NotNull List<? extends PsiVariable> outputParameters) {
|
||||
this(pattern, parameters, returnValue, outputParameters, false, null);
|
||||
this(pattern, parameters, returnValue, outputParameters, MatchType.EXACT, null);
|
||||
}
|
||||
|
||||
public DuplicatesFinder(final PsiElement[] pattern,
|
||||
@@ -435,10 +435,16 @@ public class DuplicatesFinder {
|
||||
@Nullable
|
||||
private Boolean matchParameter(@NotNull PsiElement pattern, @NotNull PsiElement candidate, @NotNull Match match) {
|
||||
final Parameter parameter = pattern.getUserData(PARAMETER);
|
||||
if (parameter == null || myWithExtractedParameters && !parameter.isReferencedBy(pattern)) {
|
||||
if (parameter == null || myMatchType == MatchType.EXACT && parameter.isFolded()) {
|
||||
return null;
|
||||
}
|
||||
return match.putParameter(parameter, candidate);
|
||||
if (!match.putParameter(parameter, candidate)) {
|
||||
return false;
|
||||
}
|
||||
if (parameter.isFolded() && pattern instanceof PsiExpression && candidate instanceof PsiExpression) {
|
||||
match.putFoldedExpressionMapping(parameter, (PsiExpression)pattern, (PsiExpression)candidate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -721,7 +727,7 @@ public class DuplicatesFinder {
|
||||
|
||||
private boolean matchExtractableExpression(@Nullable PsiElement pattern, @Nullable PsiElement candidate,
|
||||
@NotNull List<PsiElement> candidates, @NotNull Match match) {
|
||||
if (!myWithExtractedParameters || !(pattern instanceof PsiExpression) || !(candidate instanceof PsiExpression)) {
|
||||
if (myMatchType == MatchType.EXACT || !(pattern instanceof PsiExpression) || !(candidate instanceof PsiExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -751,7 +757,7 @@ public class DuplicatesFinder {
|
||||
}
|
||||
|
||||
private boolean matchExtractableVariable(@NotNull PsiElement pattern, @NotNull PsiElement candidate, @NotNull Match match) {
|
||||
if (!myWithExtractedParameters || !(pattern instanceof PsiReferenceExpression) || !(candidate instanceof PsiReferenceExpression)) {
|
||||
if (myMatchType == MatchType.EXACT || !(pattern instanceof PsiReferenceExpression) || !(candidate instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
if (myPattern.length == 1 && myPattern[0] == pattern) {
|
||||
@@ -931,10 +937,16 @@ public class DuplicatesFinder {
|
||||
public static class Parameter {
|
||||
private final PsiVariable myVariable;
|
||||
private final PsiType myType;
|
||||
private final boolean myFolded;
|
||||
|
||||
public Parameter(@Nullable PsiVariable variable, @Nullable PsiType type) {
|
||||
this(variable, type, false);
|
||||
}
|
||||
|
||||
public Parameter(@Nullable PsiVariable variable, @Nullable PsiType type, boolean folded) {
|
||||
myVariable = variable;
|
||||
myType = type;
|
||||
myFolded = folded;
|
||||
}
|
||||
|
||||
public PsiVariable getVariable() {
|
||||
@@ -945,8 +957,29 @@ public class DuplicatesFinder {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public boolean isReferencedBy(@Nullable PsiElement pattern) {
|
||||
return myVariable != null && pattern instanceof PsiReferenceExpression && ((PsiReferenceExpression)pattern).resolve() == myVariable;
|
||||
public boolean isFolded() {
|
||||
return myFolded;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return myVariable + ", " + myType + (myFolded ? ", folded" : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Parameter)) return false;
|
||||
Parameter p = (Parameter)o;
|
||||
return Objects.equals(myVariable, p.myVariable) &&
|
||||
Objects.equals(myType, p.myType) &&
|
||||
myFolded == p.myFolded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(myVariable, myType, myFolded);
|
||||
}
|
||||
}
|
||||
|
||||
public enum MatchType {EXACT, PARAMETRIZED, FOLDED}
|
||||
}
|
||||
|
||||
+9
@@ -164,4 +164,13 @@ public class ExtractableExpressionPart {
|
||||
public PsiExpression getUsage() {
|
||||
return myUsage;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ExtractableExpressionPart fromUsage(@NotNull PsiExpression usage, @NotNull PsiType type) {
|
||||
PsiType usageType;
|
||||
//noinspection AssertWithSideEffects
|
||||
assert (usageType = usage.getType()) == null || type.isAssignableFrom(usageType)
|
||||
: "expected " + type.getCanonicalText() + ", got " + usageType.getCanonicalText();
|
||||
return new ExtractableExpressionPart(usage, null, null, type);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ public class ExtractedParameter {
|
||||
return type.getCanonicalText();
|
||||
}
|
||||
|
||||
private void addUsages(ExtractableExpressionPart patternPart) {
|
||||
public void addUsages(ExtractableExpressionPart patternPart) {
|
||||
myPatternUsages.add(patternPart.getUsage());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ package com.intellij.refactoring.util.duplicates;
|
||||
import com.intellij.codeInsight.PsiEquivalenceUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
@@ -44,13 +42,14 @@ public final class Match {
|
||||
private final PsiElement myMatchStart;
|
||||
private final PsiElement myMatchEnd;
|
||||
private final Map<PsiVariable, List<PsiElement>> myParameterValues = new HashMap<>();
|
||||
private final Map<PsiVariable, ArrayList<PsiElement>> myParameterOccurrences = new HashMap<>();
|
||||
private final Map<PsiVariable, List<PsiElement>> myParameterOccurrences = new HashMap<>();
|
||||
private final Map<PsiElement, PsiElement> myDeclarationCorrespondence = new HashMap<>();
|
||||
private ReturnValue myReturnValue;
|
||||
private Ref<PsiExpression> myInstanceExpression;
|
||||
final Map<PsiVariable, PsiType> myChangedParams = new HashMap<>();
|
||||
private final boolean myIgnoreParameterTypes;
|
||||
private final List<ExtractedParameter> myExtractedParameters = new ArrayList<>();
|
||||
private final Map<DuplicatesFinder.Parameter, List<Pair.NonNull<PsiExpression, PsiExpression>>> myFoldedExpressionMappings = new HashMap<>();
|
||||
|
||||
Match(PsiElement start, PsiElement end, boolean ignoreParameterTypes) {
|
||||
LOG.assertTrue(start.getParent() == end.getParent());
|
||||
@@ -427,4 +426,15 @@ public final class Match {
|
||||
public List<ExtractedParameter> getExtractedParameters() {
|
||||
return myExtractedParameters;
|
||||
}
|
||||
|
||||
public void putFoldedExpressionMapping(@NotNull DuplicatesFinder.Parameter parameter,
|
||||
@NotNull PsiExpression pattern,
|
||||
@NotNull PsiExpression candidate) {
|
||||
myFoldedExpressionMappings.computeIfAbsent(parameter, unused -> new ArrayList<>()).add(Pair.createNonNull(pattern, candidate));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Pair.NonNull<PsiExpression, PsiExpression>> getFoldedExpressionMappings(@NotNull DuplicatesFinder.Parameter parameter) {
|
||||
return myFoldedExpressionMappings.get(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
+54
-38
@@ -114,7 +114,7 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
protected boolean myCanBeStatic;
|
||||
protected boolean myCanBeChainedConstructor;
|
||||
protected boolean myIsChainedConstructor;
|
||||
private List<Match> myDuplicates;
|
||||
protected List<Match> myDuplicates;
|
||||
private ParametrizedDuplicates myParametrizedDuplicates;
|
||||
@PsiModifier.ModifierConstant protected String myMethodVisibility = PsiModifier.PRIVATE;
|
||||
protected boolean myGenerateConditionalExit;
|
||||
@@ -890,35 +890,13 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
chooseAnchor();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DuplicatesFinder initDuplicates() {
|
||||
PsiElement[] elements = StreamEx.of(myElements)
|
||||
.filter(element -> !(element instanceof PsiWhiteSpace || element instanceof PsiComment))
|
||||
.toArray(PsiElement[]::new);
|
||||
|
||||
if (myExpression != null) {
|
||||
DuplicatesFinder finder = new DuplicatesFinder(elements, myInputVariables.copy(), Collections.emptyList());
|
||||
myDuplicates = finder.findDuplicates(myTargetClass);
|
||||
myParametrizedDuplicates = ParametrizedDuplicates.findDuplicates(this);
|
||||
return finder;
|
||||
}
|
||||
else if (elements.length != 0) {
|
||||
DuplicatesFinder finder = new DuplicatesFinder(elements, myInputVariables.copy(),
|
||||
myOutputVariable != null ? new VariableReturnValue(myOutputVariable) : null,
|
||||
Arrays.asList(myOutputVariables));
|
||||
myDuplicates = finder.findDuplicates(myTargetClass);
|
||||
myParametrizedDuplicates = ParametrizedDuplicates.findDuplicates(this);
|
||||
return finder;
|
||||
} else {
|
||||
myDuplicates = new ArrayList<>();
|
||||
}
|
||||
return null;
|
||||
protected void initDuplicates() {
|
||||
myParametrizedDuplicates = ParametrizedDuplicates.findDuplicates(this);
|
||||
myDuplicates = new ArrayList<>();
|
||||
}
|
||||
|
||||
private int estimateDuplicatesCount() {
|
||||
PsiElement[] elements = StreamEx.of(myElements)
|
||||
.filter(element -> !(element instanceof PsiWhiteSpace || element instanceof PsiComment))
|
||||
.toArray(PsiElement[]::new);
|
||||
PsiElement[] elements = getFilteredElements();
|
||||
|
||||
ReturnValue value;
|
||||
List<PsiVariable> parameters;
|
||||
@@ -944,6 +922,13 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiElement[] getFilteredElements() {
|
||||
return StreamEx.of(myElements)
|
||||
.filter(e -> !(e instanceof PsiWhiteSpace || e instanceof PsiComment || e instanceof PsiEmptyStatement))
|
||||
.toArray(PsiElement.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
public void doExtract() throws IncorrectOperationException {
|
||||
|
||||
PsiMethod newMethod = generateEmptyMethod();
|
||||
@@ -1369,7 +1354,7 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
methodCallExpression.getArgumentList().add(myElementFactory.createExpressionFromText(data.variable.getName(), methodCallExpression));
|
||||
}
|
||||
}
|
||||
List<String> reusedVariables = findReusedVariables(match, myOutputVariable);
|
||||
List<String> reusedVariables = findReusedVariables(match, myInputVariables, myOutputVariable);
|
||||
PsiElement replacedMatch = match.replace(myExtractedMethod, methodCallExpression, myOutputVariable);
|
||||
|
||||
PsiElement appendLocation = addNotNullConditionalCheck(match, replacedMatch);
|
||||
@@ -1378,7 +1363,9 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> findReusedVariables(@NotNull Match match, @Nullable PsiVariable outputVariable) {
|
||||
private static List<String> findReusedVariables(@NotNull Match match,
|
||||
@NotNull InputVariables inputVariables,
|
||||
@Nullable PsiVariable outputVariable) {
|
||||
Set<PsiLocalVariable> ignoreVariables = Collections.emptySet();
|
||||
ReturnValue returnValue = match.getOutputVariableValue(outputVariable);
|
||||
if (returnValue instanceof VariableReturnValue) {
|
||||
@@ -1388,7 +1375,7 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
}
|
||||
}
|
||||
List<ReusedLocalVariable> reusedLocalVariables =
|
||||
ReusedLocalVariablesFinder.findReusedLocalVariables(match.getMatchStart(), match.getMatchEnd(), ignoreVariables);
|
||||
ReusedLocalVariablesFinder.findReusedLocalVariables(match.getMatchStart(), match.getMatchEnd(), ignoreVariables, inputVariables);
|
||||
if (!reusedLocalVariables.isEmpty()) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (ReusedLocalVariable variable : reusedLocalVariables) {
|
||||
@@ -2159,22 +2146,29 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
|
||||
public boolean initParametrizedDuplicates(boolean showDialog) {
|
||||
if (myExtractedMethod != null && myParametrizedDuplicates != null) {
|
||||
if (!showDialog ||
|
||||
myDuplicates = myParametrizedDuplicates.getDuplicates();
|
||||
if (myDuplicates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
boolean isSignatureUnchanged = myDuplicates.stream()
|
||||
.map(Match::getExtractedParameters)
|
||||
.allMatch(List::isEmpty);
|
||||
boolean isFoldable = myInputVariables.isFoldable();
|
||||
if (!showDialog || isSignatureUnchanged || isFoldable ||
|
||||
ApplicationManager.getApplication().isUnitTestMode() ||
|
||||
new SignatureSuggesterPreviewDialog(myExtractedMethod, myParametrizedDuplicates.getParametrizedMethod(),
|
||||
myMethodCall, myParametrizedDuplicates.getParametrizedCall(),
|
||||
myParametrizedDuplicates.getSize()).showAndGet()) {
|
||||
|
||||
myDuplicates = myParametrizedDuplicates.getDuplicates();
|
||||
Runnable replaceMethod = () -> {
|
||||
myExtractedMethod = myParametrizedDuplicates.replaceMethod(myExtractedMethod);
|
||||
myMethodCall = myParametrizedDuplicates.replaceCall(myMethodCall);
|
||||
};
|
||||
if (myExtractedMethod.isPhysical()) {
|
||||
WriteCommandAction.runWriteCommandAction(myProject, replaceMethod);
|
||||
WriteCommandAction.runWriteCommandAction(myProject, () -> {
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
|
||||
replaceParametrizedMethod();
|
||||
});
|
||||
}
|
||||
else {
|
||||
replaceMethod.run();
|
||||
replaceParametrizedMethod();
|
||||
}
|
||||
myVariableDatum = myParametrizedDuplicates.getVariableDatum();
|
||||
return true;
|
||||
@@ -2183,8 +2177,15 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void replaceParametrizedMethod() {
|
||||
LOG.assertTrue(myParametrizedDuplicates != null);
|
||||
myExtractedMethod = myParametrizedDuplicates.replaceMethod(myExtractedMethod);
|
||||
myMethodCall = myParametrizedDuplicates.replaceCall(myMethodCall);
|
||||
}
|
||||
|
||||
public boolean hasDuplicates(Set<VirtualFile> files) {
|
||||
final DuplicatesFinder finder = initDuplicates();
|
||||
initDuplicates();
|
||||
final DuplicatesFinder finder = getExactDuplicatesFinder();
|
||||
|
||||
final Boolean hasDuplicates = hasDuplicates();
|
||||
if (hasDuplicates == null || hasDuplicates) return true;
|
||||
@@ -2197,6 +2198,21 @@ public class ExtractMethodProcessor implements MatchProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected DuplicatesFinder getExactDuplicatesFinder() {
|
||||
DuplicatesFinder finder = null;
|
||||
PsiElement[] elements = getFilteredElements();
|
||||
if (myExpression != null) {
|
||||
finder = new DuplicatesFinder(elements, myInputVariables.copy(), Collections.emptyList());
|
||||
}
|
||||
else if (elements.length != 0) {
|
||||
finder = new DuplicatesFinder(elements, myInputVariables.copy(),
|
||||
myOutputVariable != null ? new VariableReturnValue(myOutputVariable) : null,
|
||||
Arrays.asList(myOutputVariables));
|
||||
}
|
||||
return finder;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getConfirmDuplicatePrompt(Match match) {
|
||||
final boolean needToBeStatic = RefactoringUtil.isInStaticContext(match.getMatchStart(), myExtractedMethod.getContainingClass());
|
||||
|
||||
+1
-1
@@ -203,7 +203,7 @@ public class JavaDuplicatesExtractMethodProcessor extends ExtractMethodProcessor
|
||||
ReturnValue returnValue = myOutputVariables.length == 1 ? new VariableReturnValue(myOutputVariables[0]) : null;
|
||||
|
||||
Set<PsiVariable> effectivelyLocal = getEffectivelyLocalVariables();
|
||||
return new DuplicatesFinder(myElements, myInputVariables, returnValue, Collections.emptyList(), true, effectivelyLocal);
|
||||
return new DuplicatesFinder(myElements, myInputVariables, returnValue, Collections.emptyList(), DuplicatesFinder.MatchType.PARAMETRIZED, effectivelyLocal);
|
||||
}
|
||||
|
||||
private void relaxMethodVisibility(Match match) {
|
||||
|
||||
+108
-31
@@ -17,11 +17,11 @@ package com.intellij.refactoring.extractMethod;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.introduceField.ElementToWorkOn;
|
||||
@@ -59,7 +59,7 @@ public class ParametrizedDuplicates {
|
||||
LOG.assertTrue(pattern.length != 0, "pattern length");
|
||||
if (pattern[0] instanceof PsiStatement) {
|
||||
PsiElement[] copy = copyElements(pattern);
|
||||
myElements = wrapWithCodeBlock(copy);
|
||||
myElements = wrapWithCodeBlock(copy, originalProcessor.getInputVariables());
|
||||
}
|
||||
else if (pattern[0] instanceof PsiExpression) {
|
||||
PsiElement[] copy = copyElements(pattern);
|
||||
@@ -82,44 +82,119 @@ public class ParametrizedDuplicates {
|
||||
if (pattern.length == 0) {
|
||||
return null;
|
||||
}
|
||||
List<Match> matches = findOriginalDuplicates(originalProcessor);
|
||||
|
||||
DuplicatesFinder finder = createDuplicatesFinder(originalProcessor, DuplicatesFinder.MatchType.PARAMETRIZED);
|
||||
List<Match> matches = finder.findDuplicates(originalProcessor.myTargetClass);
|
||||
matches = filterNestedSubexpressions(matches);
|
||||
if (matches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<PsiExpression, String> predefinedNames = foldParameters(originalProcessor, matches);
|
||||
|
||||
ParametrizedDuplicates duplicates = new ParametrizedDuplicates(pattern, originalProcessor);
|
||||
if (!duplicates.initMatches(matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!duplicates.extract(originalProcessor)) {
|
||||
if (!duplicates.extract(originalProcessor, predefinedNames)) {
|
||||
return null;
|
||||
}
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Match> findOriginalDuplicates(@NotNull ExtractMethodProcessor processor) {
|
||||
private static Map<PsiExpression, String> foldParameters(ExtractMethodProcessor originalProcessor, List<Match> matches) {
|
||||
if (matches.isEmpty() || !originalProcessor.getInputVariables().isFoldable()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// As folded parameters don't work along with extracted parameters we need to apply the finder again to actually fold the parameters
|
||||
DuplicatesFinder finder = createDuplicatesFinder(originalProcessor, DuplicatesFinder.MatchType.FOLDED);
|
||||
Map<Match, Match> foldedMatches = new HashMap<>();
|
||||
Map<DuplicatesFinder.Parameter, VariableData> parametersToFold = new LinkedHashMap<>();
|
||||
for (VariableData data : originalProcessor.getInputVariables().getInputVariables()) {
|
||||
parametersToFold.put(new DuplicatesFinder.Parameter(data.variable, data.type, true), data);
|
||||
}
|
||||
|
||||
for (Match match : matches) {
|
||||
Match foldedMatch = finder.isDuplicate(match.getMatchStart(), false);
|
||||
LOG.assertTrue(foldedMatch != null, "folded match should exist");
|
||||
LOG.assertTrue(match.getMatchStart() == foldedMatch.getMatchStart() &&
|
||||
match.getMatchEnd() == foldedMatch.getMatchEnd(), "folded match range should be the same");
|
||||
foldedMatches.put(match, foldedMatch);
|
||||
|
||||
parametersToFold.keySet().removeIf(parameter -> !canFoldParameter(match, foldedMatch, parameter));
|
||||
}
|
||||
if (parametersToFold.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<PsiExpression, String> predefinedNames = new HashMap<>();
|
||||
for (Match match : matches) {
|
||||
Match foldedMatch = foldedMatches.get(match);
|
||||
LOG.assertTrue(foldedMatch != null, "folded match");
|
||||
|
||||
for (Map.Entry<DuplicatesFinder.Parameter, VariableData> entry : parametersToFold.entrySet()) {
|
||||
DuplicatesFinder.Parameter parameter = entry.getKey();
|
||||
VariableData variableData = entry.getValue();
|
||||
List<Pair.NonNull<PsiExpression, PsiExpression>> expressionMappings = foldedMatch.getFoldedExpressionMappings(parameter);
|
||||
LOG.assertTrue(!ContainerUtil.isEmpty(expressionMappings), "foldedExpressionMappings can't be empty");
|
||||
PsiType type = parameter.getType();
|
||||
|
||||
ExtractedParameter extractedParameter = null;
|
||||
for (Pair.NonNull<PsiExpression, PsiExpression> expressionMapping : expressionMappings) {
|
||||
PsiExpression patternExpression = expressionMapping.getFirst();
|
||||
ExtractableExpressionPart patternPart = ExtractableExpressionPart.fromUsage(patternExpression, type);
|
||||
if (extractedParameter == null) {
|
||||
PsiExpression candidateExpression = expressionMapping.getSecond();
|
||||
ExtractableExpressionPart candidatePart = ExtractableExpressionPart.fromUsage(candidateExpression, type);
|
||||
extractedParameter = new ExtractedParameter(patternPart, candidatePart, type);
|
||||
}
|
||||
else {
|
||||
extractedParameter.addUsages(patternPart);
|
||||
}
|
||||
predefinedNames.put(patternExpression, variableData.name);
|
||||
}
|
||||
LOG.assertTrue(extractedParameter != null, "extractedParameter can't be null");
|
||||
match.getExtractedParameters().add(extractedParameter);
|
||||
}
|
||||
}
|
||||
|
||||
return predefinedNames;
|
||||
}
|
||||
|
||||
private static boolean canFoldParameter(Match match, Match foldedMatch, DuplicatesFinder.Parameter parameter) {
|
||||
List<Pair.NonNull<PsiExpression, PsiExpression>> expressionMappings = foldedMatch.getFoldedExpressionMappings(parameter);
|
||||
if (ContainerUtil.isEmpty(expressionMappings)) {
|
||||
return false;
|
||||
}
|
||||
// Extracted parameters and folded parameters shouldn't overlap
|
||||
for (Pair.NonNull<PsiExpression, PsiExpression> expressionMapping : expressionMappings) {
|
||||
PsiExpression patternExpression = expressionMapping.getFirst();
|
||||
for (ExtractedParameter extractedParameter : match.getExtractedParameters()) {
|
||||
for (PsiExpression extractedUsage : extractedParameter.myPatternUsages) {
|
||||
if (PsiTreeUtil.isAncestor(patternExpression, extractedUsage, false) ||
|
||||
PsiTreeUtil.isAncestor(extractedUsage, patternExpression, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static DuplicatesFinder createDuplicatesFinder(@NotNull ExtractMethodProcessor processor,
|
||||
@NotNull DuplicatesFinder.MatchType matchType) {
|
||||
PsiElement[] elements = getFilteredElements(processor.myElements);
|
||||
Set<PsiVariable> effectivelyLocal = processor.getEffectivelyLocalVariables();
|
||||
|
||||
List<PsiVariable> variables = ContainerUtil.map(processor.myInputVariables.getInputVariables(), iv -> iv.variable);
|
||||
InputVariables inputVariables = new InputVariables(variables, processor.myProject, new LocalSearchScope(processor.myElements), false);
|
||||
DuplicatesFinder finder = new DuplicatesFinder(elements, inputVariables,
|
||||
processor.myOutputVariable != null
|
||||
? new VariableReturnValue(processor.myOutputVariable) : null,
|
||||
Collections.emptyList(), true, effectivelyLocal) {
|
||||
@Override
|
||||
protected boolean isSelf(@NotNull PsiElement candidate) {
|
||||
for (PsiElement element : elements) {
|
||||
if (PsiTreeUtil.isAncestor(element, candidate, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return finder.findDuplicates(processor.myTargetClass);
|
||||
InputVariables inputVariables = matchType == DuplicatesFinder.MatchType.PARAMETRIZED
|
||||
? processor.myInputVariables.copyWithoutFolding() : processor.myInputVariables;
|
||||
ReturnValue returnValue = processor.myOutputVariable != null ? new VariableReturnValue(processor.myOutputVariable) : null;
|
||||
return new DuplicatesFinder(elements, inputVariables, returnValue,
|
||||
Collections.emptyList(), matchType, effectivelyLocal);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -142,7 +217,6 @@ public class ParametrizedDuplicates {
|
||||
if (myElements.length == 0) {
|
||||
return false;
|
||||
}
|
||||
matches = filterNestedSubexpressions(matches);
|
||||
|
||||
myUsagesList = new ArrayList<>();
|
||||
Map<PsiExpression, ClusterOfUsages> usagesMap = new THashMap<>();
|
||||
@@ -234,12 +308,13 @@ public class ParametrizedDuplicates {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean extract(@NotNull ExtractMethodProcessor originalProcessor) {
|
||||
private boolean extract(@NotNull ExtractMethodProcessor originalProcessor, @NotNull Map<PsiExpression, String> predefinedNames) {
|
||||
Map<PsiExpression, PsiExpression> expressionsMapping = new THashMap<>();
|
||||
Map<PsiVariable, PsiVariable> variablesMapping = new THashMap<>();
|
||||
collectCopyMapping(originalProcessor.myElements, myElements, myUsagesList, expressionsMapping, variablesMapping);
|
||||
|
||||
Map<PsiLocalVariable, ClusterOfUsages> parameterDeclarations = createParameterDeclarations(originalProcessor, expressionsMapping);
|
||||
Map<PsiLocalVariable, ClusterOfUsages> parameterDeclarations =
|
||||
createParameterDeclarations(originalProcessor, expressionsMapping, predefinedNames);
|
||||
putMatchParameters(parameterDeclarations);
|
||||
|
||||
JavaDuplicatesExtractMethodProcessor parametrizedProcessor = new JavaDuplicatesExtractMethodProcessor(myElements, REFACTORING_NAME);
|
||||
@@ -261,8 +336,8 @@ public class ParametrizedDuplicates {
|
||||
@NotNull Map<PsiVariable, PsiVariable> variablesMapping) {
|
||||
Map<PsiVariable, PsiVariable> reverseMapping = ContainerUtil.reverseMap(variablesMapping);
|
||||
return StreamEx.of(variableDatum)
|
||||
.map(data -> data.substitute(reverseMapping.get(data.variable)))
|
||||
.toArray(VariableData[]::new);
|
||||
.map(data -> data.substitute(reverseMapping.get(data.variable)))
|
||||
.toArray(VariableData[]::new);
|
||||
}
|
||||
|
||||
private static void replaceArguments(@NotNull Map<PsiLocalVariable, ClusterOfUsages> parameterDeclarations,
|
||||
@@ -321,11 +396,11 @@ public class ParametrizedDuplicates {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement[] wrapWithCodeBlock(@NotNull PsiElement[] elements) {
|
||||
private static PsiElement[] wrapWithCodeBlock(@NotNull PsiElement[] elements, @NotNull InputVariables inputVariables) {
|
||||
PsiElement fragmentStart = elements[0];
|
||||
PsiElement fragmentEnd = elements[elements.length - 1];
|
||||
List<ReusedLocalVariable> reusedLocalVariables =
|
||||
ReusedLocalVariablesFinder.findReusedLocalVariables(fragmentStart, fragmentEnd, Collections.emptySet());
|
||||
ReusedLocalVariablesFinder.findReusedLocalVariables(fragmentStart, fragmentEnd, Collections.emptySet(), inputVariables);
|
||||
|
||||
PsiElement parent = fragmentStart.getParent();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(fragmentStart.getProject());
|
||||
@@ -424,7 +499,8 @@ public class ParametrizedDuplicates {
|
||||
|
||||
@NotNull
|
||||
private Map<PsiLocalVariable, ClusterOfUsages> createParameterDeclarations(@NotNull ExtractMethodProcessor originalProcessor,
|
||||
@NotNull Map<PsiExpression, PsiExpression> expressionsMapping) {
|
||||
@NotNull Map<PsiExpression, PsiExpression> expressionsMapping,
|
||||
@NotNull Map<PsiExpression, String> predefinedNames) {
|
||||
|
||||
Project project = myElements[0].getProject();
|
||||
Map<PsiLocalVariable, ClusterOfUsages> parameterDeclarations = new THashMap<>();
|
||||
@@ -441,8 +517,9 @@ public class ParametrizedDuplicates {
|
||||
PsiExpression patternUsage = parameter.myPattern.getUsage();
|
||||
String initializerText = patternUsage.getText();
|
||||
PsiExpression initializer = factory.createExpressionFromText(initializerText, parent);
|
||||
String predefinedName = predefinedNames.get(patternUsage);
|
||||
final SuggestedNameInfo info =
|
||||
JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.PARAMETER, null, initializer, null);
|
||||
JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.PARAMETER, predefinedName, initializer, null);
|
||||
final String parameterName = generator.generateUniqueName(info.names.length > 0 ? info.names[0] : "p");
|
||||
|
||||
String declarationText = parameter.getLocalVariableTypeText() + " " + parameterName + " = " + initializerText + ";";
|
||||
|
||||
+3
-2
@@ -33,7 +33,8 @@ public class ReusedLocalVariablesFinder {
|
||||
|
||||
public static List<ReusedLocalVariable> findReusedLocalVariables(@NotNull PsiElement fragmentStart,
|
||||
@NotNull PsiElement fragmentEnd,
|
||||
@NotNull Set<PsiLocalVariable> ignoreVariables) {
|
||||
@NotNull Set<PsiLocalVariable> ignoreVariables,
|
||||
@NotNull InputVariables inputVariables) {
|
||||
List<PsiLocalVariable> declaredVariables = getDeclaredVariables(fragmentStart, fragmentEnd, ignoreVariables);
|
||||
if (declaredVariables.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
@@ -50,7 +51,7 @@ public class ReusedLocalVariablesFinder {
|
||||
}
|
||||
|
||||
List<ReusedLocalVariable> result = new ArrayList<>();
|
||||
Set<String> tempNames = new HashSet<>();
|
||||
Set<String> tempNames = new HashSet<>(ContainerUtil.map(inputVariables.getInputVariables(), data -> data.name));
|
||||
for (PsiLocalVariable variable : reusedVariables) {
|
||||
String name = variable.getName();
|
||||
if (name == null) {
|
||||
|
||||
+12
@@ -748,6 +748,18 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDuplicates() {
|
||||
myDuplicates = Optional.ofNullable(getExactDuplicatesFinder())
|
||||
.map(finder -> finder.findDuplicates(myTargetClass))
|
||||
.orElse(new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initParametrizedDuplicates(boolean showDialog) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean insertNotNullCheckIfPossible() {
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ class C {
|
||||
}
|
||||
|
||||
private void newMethod(int i) {
|
||||
if (i < 10){
|
||||
if (i < 10) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ class C {
|
||||
}
|
||||
|
||||
private boolean newMethod(int i) {
|
||||
if (i < 10){
|
||||
if (i < 10) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
-3
@@ -7,9 +7,9 @@ class Test10 {
|
||||
|
||||
private void newMethod() {
|
||||
new Object() {
|
||||
int get() {
|
||||
return 0;
|
||||
}
|
||||
int get() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ class Test
|
||||
}
|
||||
|
||||
private boolean newMethod() {
|
||||
if(test1()) return true;
|
||||
if(test2()) return true;
|
||||
if (test1()) return true;
|
||||
if (test2()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ class Test10 {
|
||||
|
||||
private void newMethod() {
|
||||
new Super() {
|
||||
int get() {
|
||||
return 0;
|
||||
}
|
||||
int get() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
<selection>
|
||||
String s = a[j];
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(1) + "X");
|
||||
</selection>
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
String s = a[k];
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(2) + "Y");
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
<selection>
|
||||
if (a[j] == null) return;
|
||||
String s = a[j];
|
||||
System.out.println(s.charAt(1) + "X");
|
||||
</selection>
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
if (a[k] == null) return;
|
||||
String s = a[k];
|
||||
System.out.println(s.charAt(2) + "Y");
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
|
||||
String s = newMethod(a[j], 1, "X");
|
||||
if (s == null) return;
|
||||
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String newMethod(String s2, int i, String x) {
|
||||
if (s2 == null) return null;
|
||||
String s = s2;
|
||||
System.out.println(s.charAt(i) + x);
|
||||
return s;
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
String s = newMethod(a[k], 2, "Y");
|
||||
if (s == null) return;
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
|
||||
String s = newMethod(a[j], 1, "X");
|
||||
if (s == null) return;
|
||||
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String newMethod(String s1, int i, String x) {
|
||||
String s = s1;
|
||||
if (s == null) return null;
|
||||
System.out.println(s.charAt(i) + x);
|
||||
return s;
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
String s = newMethod(a[k], 2, "Y");
|
||||
if (s == null) return;
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import java.util.List;
|
||||
|
||||
class DeclaredOutputVariable {
|
||||
void foo(List<String> a, int j) {
|
||||
<selection>
|
||||
String s = a.get(j);
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(1) + "X");
|
||||
</selection>
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(List<String> a, int k) {
|
||||
String s = a.get(k);
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(2) + "Y");
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class DeclaredOutputVariable {
|
||||
void foo(List<String> a, int j) {
|
||||
|
||||
String s = newMethod(a, j, 1, "X");
|
||||
if (s == null) return;
|
||||
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String newMethod(List<String> a, int j, int i, String x) {
|
||||
String s = a.get(j);
|
||||
if (s == null) return null;
|
||||
System.out.println(s.charAt(i) + x);
|
||||
return s;
|
||||
}
|
||||
|
||||
void bar(List<String> a, int k) {
|
||||
String s = newMethod(a, k, 2, "Y");
|
||||
if (s == null) return;
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
<selection>
|
||||
String s = a[j];
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(1) + "X");
|
||||
</selection>
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
String s = a[k];
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(2) + "Y");
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(String[] a, int n) {
|
||||
String s = a[n];
|
||||
if (s == null) return;
|
||||
System.out.println(s.charAt(1) + "Z");
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class DeclaredOutputVariable {
|
||||
void foo(String[] a, int j) {
|
||||
|
||||
String s = newMethod(a[j], 1, "X");
|
||||
if (s == null) return;
|
||||
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String newMethod(String s1, int i, String x) {
|
||||
String s = s1;
|
||||
if (s == null) return null;
|
||||
System.out.println(s.charAt(i) + x);
|
||||
return s;
|
||||
}
|
||||
|
||||
void bar(String[] a, int k) {
|
||||
String s = newMethod(a[k], 2, "Y");
|
||||
if (s == null) return;
|
||||
System.out.println(s.length());
|
||||
}
|
||||
|
||||
void bar(String[] a, int n) {
|
||||
String s = newMethod(a[n], 1, "Z");
|
||||
if (s == null) return;
|
||||
System.out.println(s.length());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
class Triple {
|
||||
void foo(int... x) {
|
||||
<selection>
|
||||
System.out.println(Arrays.toString(x)); // original fragment
|
||||
System.out.println(1);
|
||||
</selection>
|
||||
|
||||
System.out.println(Arrays.toString(x)); // first duplicate
|
||||
System.out.println(2);
|
||||
|
||||
System.out.println(Arrays.toString(new int[]{})); // second duplicate
|
||||
System.out.println(1);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import java.util.Arrays;
|
||||
|
||||
class Triple {
|
||||
void foo(int... x) {
|
||||
|
||||
newMethod(x, 1);
|
||||
|
||||
|
||||
newMethod(x, 2);
|
||||
|
||||
newMethod(new int[]{}, 1);
|
||||
}
|
||||
|
||||
private void newMethod(int[] x, int i) {
|
||||
System.out.println(Arrays.toString(x)); // original fragment
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
@@ -893,6 +893,26 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
|
||||
doDuplicatesTest();
|
||||
}
|
||||
|
||||
public void testParametrizedDuplicateFoldListElement() throws Exception {
|
||||
doDuplicatesTest();
|
||||
}
|
||||
|
||||
public void testParametrizedDuplicateFoldArrayElement() throws Exception {
|
||||
doDuplicatesTest();
|
||||
}
|
||||
|
||||
public void testParametrizedMultiDuplicatesFoldArrayElement() throws Exception {
|
||||
doDuplicatesTest();
|
||||
}
|
||||
|
||||
public void testParametrizedDuplicateFoldArrayElementTwoUsages() throws Exception {
|
||||
doDuplicatesTest();
|
||||
}
|
||||
|
||||
public void testTripleParametrizedDuplicate() 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");
|
||||
|
||||
Reference in New Issue
Block a user