mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
move instance method processor: expand method reference to lambda when needed
This commit is contained in:
+45
-22
@@ -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();
|
||||
}
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+5
-7
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user