safe delete: suggest to delete constructor parameter (IDEA-173595)

when field is deleted and parameter would be unused after removing of assignment
This commit is contained in:
Anna Kozlova
2017-06-20 11:32:03 +03:00
parent 9a3223a6f5
commit 39815e1c7a
6 changed files with 115 additions and 39 deletions
@@ -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<PsiElement> findFieldUsages(final PsiField psiField, final List<UsageInfo> usages, final PsiElement[] allElementsToDelete) {
final Condition<PsiElement> isInsideDeleted = getUsageInsideDeletedFilter(allElementsToDelete);
Set<PsiParameter> 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<UsageInfo> 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));
}
}
}
@@ -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<PsiParameter> 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<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> 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<PsiMethod> 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<PsiParameter> 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;
}
}
}
@@ -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;
}
}
@@ -0,0 +1,7 @@
class MyTest {
String my<caret>Str;
public MyTest(String str) {
myStr = str;
}
}
@@ -0,0 +1,5 @@
class MyTest {
public MyTest() {
}
}
@@ -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");
}