convert static method to instance: expand method reference to lambda, fix qualifier when needed

This commit is contained in:
Anna.Kozlova
2017-04-12 14:09:10 +02:00
parent 1ac3125794
commit 3f7a67cdfc
8 changed files with 159 additions and 8 deletions
@@ -106,8 +106,12 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
for (final PsiReference ref : methodReferences) {
final PsiElement element = ref.getElement();
if (element instanceof PsiReferenceExpression) {
if (element.getParent() instanceof PsiMethodCallExpression) {
result.add(new MethodCallUsageInfo((PsiMethodCallExpression)element.getParent()));
PsiElement parent = element.getParent();
if (parent instanceof PsiMethodCallExpression) {
result.add(new MethodCallUsageInfo((PsiMethodCallExpression)parent));
}
else if (element instanceof PsiMethodReferenceExpression) {
result.add(new MethodReferenceUsageInfo((PsiMethodReferenceExpression)element, myMethod.getParameterList().getParameterIndex(myTargetParameter) == 0));
}
}
else if (element instanceof PsiDocTagValue) {
@@ -188,6 +192,9 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
}
}
}
else if (usageInfo instanceof MethodReferenceUsageInfo && !((MethodReferenceUsageInfo)usageInfo).isApplicableBySecondSearch()) {
conflicts.putValue(((MethodReferenceUsageInfo)usageInfo).getExpression(), RefactoringBundle.message("expand.method.reference.warning"));
}
}
return showConflicts(conflicts, usagesIn);
@@ -216,7 +223,7 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
// Process usages
for (final UsageInfo usage : usages) {
if (usage instanceof MethodCallUsageInfo) {
processMethodCall((MethodCallUsageInfo)usage);
processMethodCall(((MethodCallUsageInfo)usage).getMethodCall());
}
else if (usage instanceof ParameterUsageInfo) {
processParameterUsage((ParameterUsageInfo)usage);
@@ -224,6 +231,9 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
else if (usage instanceof ImplementingClassUsageInfo) {
inheritors.add(((ImplementingClassUsageInfo)usage).getPsiClass());
}
else if (usage instanceof MethodReferenceUsageInfo) {
processMethodReference((MethodReferenceUsageInfo)usage);
}
}
prepareTypeParameterReplacement();
@@ -256,15 +266,43 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
myMethod.delete();
}
private void processMethodReference(MethodReferenceUsageInfo usage) {
PsiMethodReferenceExpression expression = usage.getExpression();
if (usage.isApplicableBySecondSearch()) {
PsiExpression qualifierExpression = expression.getQualifierExpression();
LOG.assertTrue(qualifierExpression != null);
qualifierExpression.replace(JavaPsiFacade.getElementFactory(myProject).createReferenceExpression(myTargetClass));
}
else {
PsiLambdaExpression lambdaExpression = LambdaRefactoringUtil.convertMethodReferenceToLambda(expression, false, true);
List<PsiExpression> returnExpressions = LambdaUtil.getReturnExpressions(lambdaExpression);
if (!returnExpressions.isEmpty()) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)returnExpressions.get(0);
processMethodCall(methodCall);
usage.setReplacement(methodCall);
}
}
}
private void fixVisibility(final PsiMethod method, final UsageInfo[] usages) throws IncorrectOperationException {
final PsiModifierList modifierList = method.getModifierList();
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(myNewVisibility)) {
for (UsageInfo usage : usages) {
PsiElement place = null;
if (usage instanceof MethodCallUsageInfo) {
final PsiElement place = usage.getElement();
if (place != null) {
VisibilityUtil.escalateVisibility(method, place);
place = usage.getElement();
}
else if (usage instanceof MethodReferenceUsageInfo) {
PsiMethodReferenceExpression expression = ((MethodReferenceUsageInfo)usage).getExpression();
if (expression != null && expression.isValid()) {
place = expression;
}
else {
place = ((MethodReferenceUsageInfo)usage).getReplacement();
}
}
if (place != null) {
VisibilityUtil.escalateVisibility(method, place);
}
}
}
@@ -364,8 +402,7 @@ public class ConvertToInstanceMethodProcessor extends BaseRefactoringProcessor {
return true;
}
private void processMethodCall(MethodCallUsageInfo usageInfo) throws IncorrectOperationException {
PsiMethodCallExpression methodCall = usageInfo.getMethodCall();
private void processMethodCall(final PsiMethodCallExpression methodCall) throws IncorrectOperationException {
PsiParameterList parameterList = myMethod.getParameterList();
PsiElementFactory factory = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory();
int parameterIndex = parameterList.getParameterIndex(myTargetParameter);
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.convertToInstanceMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiMethodReferenceExpression;
import com.intellij.usageView.UsageInfo;
public class MethodReferenceUsageInfo extends UsageInfo {
private final PsiMethodReferenceExpression myExpression;
private final boolean myApplicableBySecondSearch;
private PsiMethodCallExpression myReplacement;
public MethodReferenceUsageInfo(PsiMethodReferenceExpression methodReferenceExpression, boolean bySecondSearch) {
super(methodReferenceExpression);
myExpression = methodReferenceExpression;
myApplicableBySecondSearch = bySecondSearch;
}
public PsiMethodReferenceExpression getExpression() {
return myExpression;
}
public boolean isApplicableBySecondSearch() {
return myApplicableBySecondSearch;
}
public void setReplacement(PsiMethodCallExpression replacement) {
myReplacement = replacement;
}
public PsiMethodCallExpression getReplacement() {
return myReplacement;
}
}
@@ -0,0 +1,11 @@
class Bar0 {}
class Bar {
void f() {
I r = Bar::foo;
}
private static void fo<caret>o(Bar0 bar) { }
}
interface I {
void m(Bar0 b);
}
@@ -0,0 +1,12 @@
class Bar0 {
void foo() { }
}
class Bar {
void f() {
I r = Bar0::foo;
}
}
interface I {
void m(Bar0 b);
}
@@ -0,0 +1,11 @@
class Bar0 {}
class Bar {
void f() {
I r = Bar::foo;
}
private static void fo<caret>o(Integer i, Bar0 bar) { }
}
interface I {
void m(Integer i, Bar0 b);
}
@@ -0,0 +1,12 @@
class Bar0 {
void foo(Integer i) { }
}
class Bar {
void f() {
I r = (i, bar) -> bar.foo(i);
}
}
interface I {
void m(Integer i, Bar0 b);
}
@@ -16,6 +16,7 @@
package com.intellij.refactoring.convertToInstanceMethod;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.refactoring.BaseRefactoringProcessor;
public class ConvertToInstance8MethodTest extends ConvertToInstanceMethodTest {
@Override
@@ -27,6 +28,14 @@ public class ConvertToInstance8MethodTest extends ConvertToInstanceMethodTest {
doTest(0);
}
public void testMethodReferenceAcceptableBySecondSearch() throws Exception {
doTest(0);
}
public void testMethodReferenceToLambda() throws Exception {
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest(1));
}
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_1_8;
@@ -584,6 +584,17 @@ public abstract class BaseRefactoringProcessor implements Runnable {
return myTestIgnore;
}
@TestOnly
public static <T extends Throwable> void withIgnoredConflicts(ThrowableRunnable<T> r) throws T {
try {
myTestIgnore = true;
r.run();
}
finally {
myTestIgnore = false;
}
}
@NotNull
public Collection<String> getMessages() {
List<String> result = new ArrayList<>(messages);