diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java index 0c595e0cf0b9..2eebd59bdd72 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java @@ -31,6 +31,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; import com.intellij.psi.search.searches.ReferencesSearch; @@ -398,6 +399,15 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { protected int getParameterIdx() { return parameterIndex; } + + @Override + protected PsiParameter getParameterInCaller(PsiMethod called, int paramIdx, PsiMethod caller) { + return delegatingParams.stream() + .filter(usage -> caller.equals(usage.getCallerMethod())) + .map(usage -> usage.getParameterInCaller()) + .findFirst() + .orElse(super.getParameterInCaller(called, paramIdx, caller)); + } }; TreeUtil.expand(chooser.getTree(), 2); if (!chooser.showAndGet()) { @@ -883,12 +893,24 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { private static Condition findFieldUsages(final PsiField psiField, final List usages, final PsiElement[] allElementsToDelete) { final Condition isInsideDeleted = getUsageInsideDeletedFilter(allElementsToDelete); + Set parameters = new LinkedHashSet<>(); ReferencesSearch.search(psiField).forEach(reference -> { if (!isInsideDeleted.value(reference.getElement())) { final PsiElement element = reference.getElement(); final PsiElement parent = element.getParent(); if (parent instanceof PsiAssignmentExpression && element == ((PsiAssignmentExpression)parent).getLExpression()) { usages.add(new SafeDeleteFieldWriteReference((PsiAssignmentExpression)parent, psiField)); + PsiExpression rExpression = PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)parent).getRExpression()); + if (rExpression instanceof PsiReferenceExpression) { + PsiElement resolve = ((PsiReferenceExpression)rExpression).resolve(); + if (resolve instanceof PsiParameter) { + PsiParameter parameter = (PsiParameter)resolve; + PsiElement scope = parameter.getDeclarationScope(); + if (scope instanceof PsiMethod && ((PsiMethod)scope).isConstructor()) { + parameters.add(parameter); + } + } + } } else { TextRange range = reference.getRangeInElement(); @@ -900,9 +922,42 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { return true; }); + PsiMethod setterPrototype = PropertyUtil.generateSetterPrototype(psiField, psiField.getContainingClass()); + PsiParameter setterParameter = setterPrototype.getParameterList().getParameters()[0]; + for (PsiParameter parameter : parameters) { + PsiElement scope = parameter.getDeclarationScope(); + if (scope instanceof PsiMethod) { + if (!ReferencesSearch.search(parameter, new LocalSearchScope(scope)).forEach(ref -> { + PsiElement element = ref.getElement(); + if (element instanceof PsiReferenceExpression) { + PsiElement parent = PsiUtil.skipParenthesizedExprUp(element.getParent()); + if (parent instanceof PsiAssignmentExpression) { + PsiExpression lExpression = PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)parent).getLExpression()); + if (lExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)lExpression).resolve() == psiField) { + return true; + } + } + } + return false; + })) continue; + usages.add(createParameterCallHierarchyUsageInfo(setterPrototype, setterParameter, (PsiMethod)scope, parameter)); + } + } + return isInsideDeleted; } + private static SafeDeleteParameterCallHierarchyUsageInfo createParameterCallHierarchyUsageInfo(PsiMethod called, + PsiParameter calledParameter, + PsiMethod caller, PsiParameter parameterInCaller) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return new SafeDeleteParameterCallHierarchyUsageInfo(caller, parameterInCaller, caller, parameterInCaller); + } + else { + return new SafeDeleteParameterCallHierarchyUsageInfo(called, calledParameter, caller, parameterInCaller); + } + } + private static void findParameterUsages(final PsiParameter parameter, final List usages) { final PsiMethod method = (PsiMethod)parameter.getDeclarationScope(); @@ -919,12 +974,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { final PsiParameter paramInCaller = SafeDeleteJavaCallerChooser.isTheOnlyOneParameterUsage(element.getParent(), parameterIndex, method); if (paramInCaller != null) { final PsiMethod callerMethod = (PsiMethod)paramInCaller.getDeclarationScope(); - if (ApplicationManager.getApplication().isUnitTestMode()) { - usages.add(new SafeDeleteParameterCallHierarchyUsageInfo(callerMethod, paramInCaller, callerMethod)); - } - else { - usages.add(new SafeDeleteParameterCallHierarchyUsageInfo(method, parameter, callerMethod)); - } + usages.add(createParameterCallHierarchyUsageInfo( method, parameter, callerMethod, paramInCaller)); } } } diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java index a4b0d7223502..6217d83d8920 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteJavaCallerChooser.java @@ -80,7 +80,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { final PsiMethod nodeMethod = methodNode.getMethod(); if (nodeMethod.equals(myMethod)) continue; final PsiParameter parameter = nodeMethod.getParameterList().getParameters()[methodNode.myParameterIdx]; - foreignMethodUsages.add(new SafeDeleteParameterCallHierarchyUsageInfo(nodeMethod, parameter, nodeMethod)); + foreignMethodUsages.add(new SafeDeleteParameterCallHierarchyUsageInfo(nodeMethod, parameter, nodeMethod, parameter)); ReferencesSearch.search(nodeMethod).forEach(reference -> { final PsiElement element = reference.getElement(); if (element != null) { @@ -191,6 +191,33 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { return null; } + protected PsiParameter getParameterInCaller(PsiMethod called, int paramIdx, PsiMethod caller) { + //do not change hierarchy + if (caller.findDeepestSuperMethods().length > 0) { + return null; + } + + //find first method call + final Ref ref = new Ref<>(); + ReferencesSearch.search(called, new LocalSearchScope(caller)).forEach(reference -> { + final PsiElement element = reference.getElement(); + if (element instanceof PsiJavaCodeReferenceElement) { + final PsiElement elementParent = element.getParent(); + if (elementParent instanceof PsiCallExpression) { + ref.set(isTheOnlyOneParameterUsage(elementParent, paramIdx, called)); + return false; + } + } + return true; + }); + return ref.get(); + } + + protected int getCallerParameterIndex(PsiMethod called, int paramIdx, PsiMethod caller) { + final PsiParameter parameter = getParameterInCaller(called, paramIdx, caller); + return parameter != null ? caller.getParameterList().getParameterIndex(parameter) : -1; + } + private class SafeDeleteJavaMethodNode extends JavaMethodNode { private final int myParameterIdx; @@ -206,7 +233,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { @Override protected MethodNodeBase createNode(PsiMethod caller, HashSet called) { - return new SafeDeleteJavaMethodNode(caller, called, myCancelCallback, getParameterIndex(caller), myProject); + return new SafeDeleteJavaMethodNode(caller, called, myCancelCallback, getCallerParameterIndex(myMethod, myParameterIdx, caller), myProject); } @Override @@ -222,35 +249,8 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser { @Override protected Condition getFilter() { - return method -> !myMethod.equals(method) && getParameter(method) != null; - } - - private PsiParameter getParameter(PsiMethod caller) { - - //do not change hierarchy - if (caller.findDeepestSuperMethods().length > 0) { - return null; - } - - //find first method call - final Ref ref = new Ref<>(); - ReferencesSearch.search(myMethod, new LocalSearchScope(caller)).forEach(reference -> { - final PsiElement element = reference.getElement(); - if (element instanceof PsiReferenceExpression) { - final PsiElement elementParent = element.getParent(); - if (elementParent instanceof PsiCallExpression) { - ref.set(isTheOnlyOneParameterUsage(elementParent, myParameterIdx, myMethod)); - return false; - } - } - return true; - }); - return ref.get(); - } - - private int getParameterIndex(PsiMethod caller) { - final PsiParameter parameter = getParameter(caller); - return parameter != null ? caller.getParameterList().getParameterIndex(parameter) : -1; + return method -> !myMethod.equals(method) && getParameterInCaller(myMethod, myParameterIdx, method) != null; } + } } diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeleteParameterCallHierarchyUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeleteParameterCallHierarchyUsageInfo.java index c223c08273e6..00061bbd27c8 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeleteParameterCallHierarchyUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeleteParameterCallHierarchyUsageInfo.java @@ -23,11 +23,16 @@ public class SafeDeleteParameterCallHierarchyUsageInfo extends SafeDeleteUsageIn private final PsiMethod myCalledMethod; private final PsiMethod myCallerMethod; + private final PsiParameter myParameterInCaller; - public SafeDeleteParameterCallHierarchyUsageInfo(PsiMethod calledMethod, PsiParameter parameter, PsiMethod callerMethod) { + public SafeDeleteParameterCallHierarchyUsageInfo(PsiMethod calledMethod, + PsiParameter parameter, + PsiMethod callerMethod, + PsiParameter parameterInCaller) { super(calledMethod, parameter); myCalledMethod = calledMethod; myCallerMethod = callerMethod; + myParameterInCaller = parameterInCaller; } @Override @@ -60,6 +65,7 @@ public class SafeDeleteParameterCallHierarchyUsageInfo extends SafeDeleteUsageIn if (!myCalledMethod.equals(info.myCalledMethod)) return false; if (!myCallerMethod.equals(info.myCallerMethod)) return false; + if (!myParameterInCaller.equals(info.myParameterInCaller)) return false; return true; } @@ -69,6 +75,11 @@ public class SafeDeleteParameterCallHierarchyUsageInfo extends SafeDeleteUsageIn int result = super.hashCode(); result = 31 * result + myCalledMethod.hashCode(); result = 31 * result + myCallerMethod.hashCode(); + result = 31 * result + myParameterInCaller.hashCode(); return result; } + + public PsiParameter getParameterInCaller() { + return myParameterInCaller; + } } diff --git a/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter.java b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter.java new file mode 100644 index 000000000000..2d99dd0dcab4 --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter.java @@ -0,0 +1,7 @@ +class MyTest { + String myStr; + + public MyTest(String str) { + myStr = str; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter_after.java b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter_after.java new file mode 100644 index 000000000000..33a8013a0fdb --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/DeepDeleteFieldAndAssignedParameter_after.java @@ -0,0 +1,5 @@ +class MyTest { + + public MyTest() { + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java index 4133d577d593..9c6125598bd2 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java @@ -91,6 +91,10 @@ public class SafeDeleteTest extends MultiFileTestCase { public void testDeepDeleteParameterOtherTypeInBinaryExpression() throws Exception { doSingleFileTest(); } + + public void testDeepDeleteFieldAndAssignedParameter() throws Exception { + doSingleFileTest(); + } public void testImpossibleToDeepDeleteParameter() throws Exception { doSingleFileTest(); @@ -132,7 +136,6 @@ public class SafeDeleteTest extends MultiFileTestCase { doTest("C2"); } - public void testTopLevelDocComment() throws Exception { doTest("foo.C1"); }