From 32dd35be57e2f149068d483ee343125720a133b6 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Mon, 29 Feb 2016 16:26:34 +0100 Subject: [PATCH] introduce parameter: extract and delete unused varargs parameter (IDEA-152294) --- ...troduceParameterMethodUsagesProcessor.java | 15 +++-- .../OldReferenceResolver.java | 66 +++++++++++++++++-- .../afterBareRefToVararg.java | 17 +++++ .../afterNewWithRefToVararg.java | 17 +++++ .../beforeBareRefToVararg.java | 16 +++++ .../beforeNewWithRefToVararg.java | 16 +++++ .../refactoring/IntroduceParameterTest.java | 8 +++ 7 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 java/java-tests/testData/refactoring/introduceParameter/afterBareRefToVararg.java create mode 100644 java/java-tests/testData/refactoring/introduceParameter/afterNewWithRefToVararg.java create mode 100644 java/java-tests/testData/refactoring/introduceParameter/beforeBareRefToVararg.java create mode 100644 java/java-tests/testData/refactoring/introduceParameter/beforeNewWithRefToVararg.java diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java index 0c1db4ec04ea..0bb10aa527a8 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java @@ -125,7 +125,7 @@ public class JavaIntroduceParameterMethodUsagesProcessor implements IntroducePar final PsiExpressionList argumentList = callExpression.getArgumentList(); LOG.assertTrue(argumentList != null, callExpression.getText()); - removeParametersFromCall(argumentList, data.getParametersToRemove()); + removeParametersFromCall(argumentList, data.getParametersToRemove(), methodToSearchFor); return false; } @@ -142,12 +142,19 @@ public class JavaIntroduceParameterMethodUsagesProcessor implements IntroducePar JavaPsiFacade.getElementFactory(project)); } - private static void removeParametersFromCall(@NotNull final PsiExpressionList argList, TIntArrayList parametersToRemove) { + private static void removeParametersFromCall(@NotNull final PsiExpressionList argList, TIntArrayList parametersToRemove, PsiMethod method) { + final int parametersCount = method.getParameterList().getParametersCount(); final PsiExpression[] exprs = argList.getExpressions(); parametersToRemove.forEachDescending(new TIntProcedure() { - public boolean execute(final int paramNum) { + public boolean execute(int paramNum) { try { - if (paramNum < exprs.length) { + //parameter was introduced before varargs + if (method.isVarArgs() && paramNum == parametersCount - 1) { + for (int i = paramNum + 1; i < exprs.length; i++) { + exprs[i].delete(); + } + } + else if (paramNum < exprs.length) { exprs[paramNum].delete(); } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java index 646be1bd7e7d..3d483ced014d 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java @@ -18,13 +18,18 @@ package com.intellij.refactoring.introduceParameter; import com.intellij.codeInsight.generation.GenerateMembersUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.IntroduceParameterRefactoring; import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -92,6 +97,10 @@ public class OldReferenceResolver { PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory(); + replaceOldRefWithNew(mappingsSet, factory); + } + + private static void replaceOldRefWithNew(Set> mappingsSet, PsiElementFactory factory) { for (Map.Entry entry : mappingsSet) { PsiExpression oldRef = entry.getKey(); PsiElement newRef = factory.createExpressionFromText(entry.getValue(), null); @@ -120,15 +129,31 @@ public class OldReferenceResolver { PsiParameterList parameterList = myMethodToReplaceIn.getParameterList(); if (subj.getParent() != parameterList) return; - int index = parameterList.getParameterIndex((PsiParameter)subj); + final PsiParameter parameter = (PsiParameter)subj; + int index = parameterList.getParameterIndex(parameter); if (index < 0) return; if (index < myActualArgs.length) { PsiExpression actualArg = myActualArgs[index]; - int copyingSafetyLevel = RefactoringUtil.verifySafeCopyExpression(actualArg); - if (copyingSafetyLevel == RefactoringUtil.EXPR_COPY_PROHIBITED) { - actualArg = factory.createExpressionFromText(getTempVar(actualArg), null); + PsiExpression initializer = actualArg; + + final PsiType parameterType = parameter.getType(); + if (parameter.isVarArgs() && parameterType instanceof PsiEllipsisType) { + final String varargsJoin = StringUtil.join(ContainerUtil.map2Array(myActualArgs, String.class, new Function() { + @Override + public String fun(PsiExpression expression) { + return expression != null ? expression.getText() : "null"; + } + }), index + 1, myActualArgs.length, ","); + String newArrayInitializer = "new " + ((PsiEllipsisType)parameterType).toArrayType().getCanonicalText() + " {" + varargsJoin + "}"; + initializer = replaceInitializerWithVarargSubstitution(factory, parameter, initializer, newArrayInitializer); + } + + if (RefactoringUtil.verifySafeCopyExpression(actualArg) == RefactoringUtil.EXPR_COPY_PROHIBITED) { + newExpr = newExpr.replace(factory.createExpressionFromText(getTempVar(actualArg, initializer), null)); + } + else { + newExpr = newExpr.replace(initializer); } - newExpr = newExpr.replace(actualArg); } } // "naked" field and methods (should become qualified) @@ -188,6 +213,31 @@ public class OldReferenceResolver { } } + @NotNull + private PsiExpression replaceInitializerWithVarargSubstitution(PsiElementFactory factory, + final PsiParameter parameter, + PsiExpression initializer, String newArrayInitializer) { + final String tempVar = getTempVar(factory.createExpressionFromText(newArrayInitializer, myContext)); + final Map map = new HashMap<>(); + if (initializer instanceof PsiReferenceExpression && Comparing.strEqual(parameter.getName(), initializer.getText())) { + initializer = factory.createExpressionFromText(tempVar, myContext); + } + else { + initializer = (PsiExpression)initializer.copy(); + initializer.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (Comparing.strEqual(parameter.getName(), expression.getText())) { + map.put(expression, tempVar); + } + } + }); + replaceOldRefWithNew(map.entrySet(), factory); + } + return initializer; + } + private PsiExpression getInstanceRef(PsiElementFactory factory) throws IncorrectOperationException { int copyingSafetyLevel = RefactoringUtil.verifySafeCopyExpression(myInstanceRef); @@ -199,12 +249,16 @@ public class OldReferenceResolver { } private String getTempVar(PsiExpression expr) throws IncorrectOperationException { + return getTempVar(expr, expr); + } + + private String getTempVar(PsiExpression expr, PsiExpression initializer) throws IncorrectOperationException { String id = myTempVars.get(expr); if (id != null) { return id; } else { - id = RefactoringUtil.createTempVar(expr, myContext, true); + id = RefactoringUtil.createTempVar(initializer, myContext, true); myTempVars.put(expr, id); return id; } diff --git a/java/java-tests/testData/refactoring/introduceParameter/afterBareRefToVararg.java b/java/java-tests/testData/refactoring/introduceParameter/afterBareRefToVararg.java new file mode 100644 index 000000000000..24120262d4ff --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameter/afterBareRefToVararg.java @@ -0,0 +1,17 @@ +class Test { + + void f(String strings) { + final String[] strings1 = new String[]{"c", "d"}; + extract("a", "b", strings1); + } + + private static void extract(final String from, final String to, String[] anObject) { + + new Foo(anObject); + } + + private static class Foo { + public Foo(String[] extensions) { + } + } +} diff --git a/java/java-tests/testData/refactoring/introduceParameter/afterNewWithRefToVararg.java b/java/java-tests/testData/refactoring/introduceParameter/afterNewWithRefToVararg.java new file mode 100644 index 000000000000..89a1cde959d9 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameter/afterNewWithRefToVararg.java @@ -0,0 +1,17 @@ +class Test { + + void f(String strings) { + final String[] strings1 = new String[]{"c", "d"}; + final Foo foo = new Foo(strings1); + extract("a", "b", foo); + } + + private static void extract(final String from, final String to, Foo anObject) { + + } + + private static class Foo { + public Foo(String[] extensions) { + } + } +} diff --git a/java/java-tests/testData/refactoring/introduceParameter/beforeBareRefToVararg.java b/java/java-tests/testData/refactoring/introduceParameter/beforeBareRefToVararg.java new file mode 100644 index 000000000000..cfa71a30ecd3 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameter/beforeBareRefToVararg.java @@ -0,0 +1,16 @@ +class Test { + + void f(String strings) { + extract("a", "b", "c", "d"); + } + + private static void extract(final String from, final String to, final String... extensions) { + + new Foo(extensions); + } + + private static class Foo { + public Foo(String[] extensions) { + } + } +} diff --git a/java/java-tests/testData/refactoring/introduceParameter/beforeNewWithRefToVararg.java b/java/java-tests/testData/refactoring/introduceParameter/beforeNewWithRefToVararg.java new file mode 100644 index 000000000000..dbab5052c86b --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceParameter/beforeNewWithRefToVararg.java @@ -0,0 +1,16 @@ +class Test { + + void f(String strings) { + extract("a", "b", "c", "d"); + } + + private static void extract(final String from, final String to, final String... extensions) { + + new Foo(extensions); + } + + private static class Foo { + public Foo(String[] extensions) { + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java index 77cb593f30b9..6c0b19f5503c 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceParameterTest.java @@ -146,6 +146,14 @@ public class IntroduceParameterTest extends LightRefactoringTestCase { doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE, false, false, false, false); } + public void testBareRefToVararg() throws Exception { + doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE, true, false, false, false); + } + + public void testNewWithRefToVararg() throws Exception { + doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE, true, false, false, false); + } + public void testSuperWithSideEffect() { doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE, false, false, false, false, "Parameter initializer contains super, but not all calls to method are in its class");