diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodProcessor.java index 26338d0d79cb..e4fde5f19f91 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodProcessor.java @@ -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 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; } diff --git a/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReference.java.after b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReference.java.after new file mode 100644 index 000000000000..fe6abb330a88 --- /dev/null +++ b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReference.java.after @@ -0,0 +1,17 @@ +interface SAM { + String m(MethodReference f, F f1); +} + + +class F { + String getX() { + return null; + } +} + +class MethodReference { + + static void test() { + SAM s = (stringMethodReference, f1) -> f1.getX(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java new file mode 100644 index 000000000000..941e2e0fea5e --- /dev/null +++ b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java @@ -0,0 +1,17 @@ + +class Bar { + void foo(int i, Bar1 b1) { + } + +} + +class Bar1 { + + void m(Bar b) { + Bar3 r = b::foo; + } +} + +interface Bar3 { + void m(int i, Bar1 v); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java.after b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java.after new file mode 100644 index 000000000000..6fce2de54dcb --- /dev/null +++ b/java/java-tests/testData/refactoring/moveInstanceMethod/MethodReferenceToExpandToLambda.java.after @@ -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); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveInstanceMethod/ParameterMethodReference.java.after b/java/java-tests/testData/refactoring/moveInstanceMethod/ParameterMethodReference.java.after index 61ff4a019980..1e2897d7db28 100644 --- a/java/java-tests/testData/refactoring/moveInstanceMethod/ParameterMethodReference.java.after +++ b/java/java-tests/testData/refactoring/moveInstanceMethod/ParameterMethodReference.java.after @@ -10,7 +10,7 @@ class Test { private final Destination destination = new Destination(); public void main(Stream stream){ - stream.filter(Test.Destination::notNull); + stream.filter(Destination::notNull); } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/moveMethod/MoveInstanceMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/moveMethod/MoveInstanceMethodTest.java index 4dc98f3c2a61..2266b70dfb68 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/moveMethod/MoveInstanceMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/moveMethod/MoveInstanceMethodTest.java @@ -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); }