move instance method processor: expand method reference to lambda when needed

This commit is contained in:
Anna.Kozlova
2017-04-12 17:31:06 +02:00
parent 17848f40a4
commit 603dfac67a
6 changed files with 103 additions and 30 deletions
@@ -28,6 +28,7 @@ import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
@@ -120,12 +121,12 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
if (myTargetVariable instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)myTargetVariable;
final int index = myMethod.getParameterList().getParameterIndex(parameter);
for (final UsageInfo usageInfo : usages) {
if (usageInfo instanceof MethodCallUsageInfo) {
final PsiElement methodCall = ((MethodCallUsageInfo)usageInfo).getMethodCallExpression();
if (methodCall instanceof PsiMethodCallExpression) {
final PsiExpression[] expressions = ((PsiMethodCallExpression)methodCall).getArgumentList().getExpressions();
final int index = myMethod.getParameterList().getParameterIndex(parameter);
if (index < expressions.length) {
PsiExpression instanceValue = expressions[index];
instanceValue = RefactoringUtil.unparenthesizeExpression(instanceValue);
@@ -136,8 +137,8 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
conflicts.putValue(instanceValue, message);
}
}
} else if (methodCall instanceof PsiMethodReferenceExpression) {
conflicts.putValue(methodCall, "Method reference would be broken after move");
} else if (methodCall instanceof PsiMethodReferenceExpression && shouldBeExpandedToLambda((PsiMethodReferenceExpression)methodCall, index)) {
conflicts.putValue(methodCall, RefactoringBundle.message("expand.method.reference.warning"));
}
}
}
@@ -151,6 +152,22 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
return showConflicts(conflicts, usages);
}
/**
* If collapse by second search is possible, then it's possible not to expand
*/
private boolean shouldBeExpandedToLambda(PsiMethodReferenceExpression referenceExpression, int index) {
PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(referenceExpression.getFunctionalInterfaceType());
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod != null) {
MethodSignature methodSignature = interfaceMethod.getSignature(LambdaUtil.getSubstitutor(interfaceMethod, resolveResult));
if (index == 0 && methodSignature.getParameterTypes().length > 0 &&
methodSignature.getParameterTypes()[0].isAssignableFrom(myMethod.getParameterList().getParameters()[0].getType())) {
return false;
}
}
return true;
}
@NotNull
protected UsageInfo[] findUsages() {
final PsiManager manager = myMethod.getManager();
@@ -220,6 +237,7 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
myTargetClass = (PsiClass) elements[2];
}
@NotNull
protected String getCommandName() {
return RefactoringBundle.message("move.instance.method.command");
}
@@ -246,20 +264,32 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
else if (expression instanceof PsiMethodReferenceExpression) {
PsiMethodReferenceExpression methodReferenceExpression = (PsiMethodReferenceExpression)expression;
PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression();
String exprText;
if (myTargetVariable instanceof PsiParameter ||
qualifierExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifierExpression).resolve() == myMethod.getContainingClass()) {
exprText = myTargetVariable.getType().getCanonicalText();
}
else if (qualifierExpression instanceof PsiReferenceExpression) {
exprText = qualifierExpression.getText() + "." + myTargetVariable.getName();
if (myTargetVariable instanceof PsiParameter && shouldBeExpandedToLambda(methodReferenceExpression, myMethod.getParameterList().getParameterIndex((PsiParameter)myTargetVariable))) {
PsiLambdaExpression lambdaExpression = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodReferenceExpression, false, true);
if (lambdaExpression != null) {
List<PsiExpression> returnExpressions = LambdaUtil.getReturnExpressions(lambdaExpression);
if (!returnExpressions.isEmpty()) {
correctMethodCall((PsiMethodCallExpression)returnExpressions.get(0), false);
}
}
}
else {
exprText = myTargetVariable.getName();
String exprText;
if (myTargetVariable instanceof PsiParameter ||
qualifierExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifierExpression).resolve() == myMethod.getContainingClass()) {
exprText = myTargetVariable.getType().getCanonicalText();
}
else if (qualifierExpression instanceof PsiReferenceExpression) {
exprText = qualifierExpression.getText() + "." + myTargetVariable.getName();
}
else {
exprText = myTargetVariable.getName();
}
PsiExpression newQualifier = JavaPsiFacade.getInstance(myProject).getElementFactory().createExpressionFromText(exprText, null);
((PsiMethodReferenceExpression)expression).setQualifierExpression(newQualifier);
JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(expression);
}
PsiExpression newQualifier = JavaPsiFacade.getInstance(myProject).getElementFactory().createExpressionFromText(exprText, null);
((PsiMethodReferenceExpression)expression).setQualifierExpression(
(PsiExpression)JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(newQualifier));
}
}
else if (usage instanceof JavadocUsageInfo) {
@@ -474,14 +504,7 @@ public class MoveInstanceMethodProcessor extends BaseRefactoringProcessor{
return;
}
if (myTargetVariable.equals(resolved)) {
PsiThisExpression thisExpression = RefactoringChangeUtil.createThisExpression(manager, PsiTreeUtil.isAncestor(myMethod,
PsiTreeUtil
.getParentOfType(
expression,
PsiClass.class),
true)
? myTargetClass
: null);
PsiThisExpression thisExpression = RefactoringChangeUtil.createThisExpression(manager, PsiTreeUtil.isAncestor(myMethod, PsiTreeUtil.getParentOfType(expression, PsiClass.class), true) ? myTargetClass : null);
replaceMap.put(expression, thisExpression);
return;
}
@@ -0,0 +1,17 @@
interface SAM {
String m(MethodReference<String> f, F f1);
}
class F {
String getX() {
return null;
}
}
class MethodReference<X> {
static void test() {
SAM s = (stringMethodReference, f1) -> f1.getX();
}
}
@@ -0,0 +1,17 @@
class Bar {
void f<caret>oo(int i, Bar1 b1) {
}
}
class Bar1 {
void m(Bar b) {
Bar3 r = b::foo;
}
}
interface Bar3 {
void m(int i, Bar1 v);
}
@@ -0,0 +1,18 @@
class Bar {
}
class Bar1 {
void m(Bar b) {
Bar3 r = (i, b1) -> b1.foo(i);
}
void foo(int i) {
}
}
interface Bar3 {
void m(int i, Bar1 v);
}
@@ -10,7 +10,7 @@ class Test {
private final Destination destination = new Destination();
public void main(Stream<Destination> stream){
stream.filter(Test.Destination::notNull);
stream.filter(Destination::notNull);
}
}
@@ -90,19 +90,17 @@ public class MoveInstanceMethodTest extends LightRefactoringTestCase {
}
public void testMethodReference() throws Exception {
try {
doTest(true, 0);
fail("Conflict was not detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals("Method reference would be broken after move", e.getMessage());
}
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest(true, 0));
}
public void testThisMethodReferenceWithTargetField() throws Exception {
doTest(false, 0);
}
public void testMethodReferenceToExpandToLambda() throws Exception {
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest(true, 1));
}
public void testForeignMethodReferenceWithTargetField() throws Exception {
doTest(false, 0);
}