From 3f7a67cdfcedfa923e6c023cf87c36b205a051c2 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Wed, 12 Apr 2017 11:24:17 +0200 Subject: [PATCH] convert static method to instance: expand method reference to lambda, fix qualifier when needed --- .../ConvertToInstanceMethodProcessor.java | 53 ++++++++++++++++--- .../MethodReferenceUsageInfo.java | 48 +++++++++++++++++ ...thodReferenceAcceptableBySecondSearch.java | 11 ++++ ...ferenceAcceptableBySecondSearch.java.after | 12 +++++ .../MethodReferenceToLambda.java | 11 ++++ .../MethodReferenceToLambda.java.after | 12 +++++ .../ConvertToInstance8MethodTest.java | 9 ++++ .../refactoring/BaseRefactoringProcessor.java | 11 ++++ 8 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/MethodReferenceUsageInfo.java create mode 100644 java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java create mode 100644 java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java.after create mode 100644 java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java create mode 100644 java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java.after diff --git a/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java index 0092c98326ad..905cd540c383 100644 --- a/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstanceMethodProcessor.java @@ -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 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); diff --git a/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/MethodReferenceUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/MethodReferenceUsageInfo.java new file mode 100644 index 000000000000..a4b6e6db95d5 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/convertToInstanceMethod/MethodReferenceUsageInfo.java @@ -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; + } +} diff --git a/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java new file mode 100644 index 000000000000..f74b6b09f393 --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java @@ -0,0 +1,11 @@ +class Bar0 {} +class Bar { + void f() { + I r = Bar::foo; + } + + private static void foo(Bar0 bar) { } +} +interface I { + void m(Bar0 b); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java.after b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java.after new file mode 100644 index 000000000000..611da468d055 --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceAcceptableBySecondSearch.java.after @@ -0,0 +1,12 @@ +class Bar0 { + void foo() { } +} +class Bar { + void f() { + I r = Bar0::foo; + } + +} +interface I { + void m(Bar0 b); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java new file mode 100644 index 000000000000..3a3fa88827f7 --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java @@ -0,0 +1,11 @@ +class Bar0 {} +class Bar { + void f() { + I r = Bar::foo; + } + + private static void foo(Integer i, Bar0 bar) { } +} +interface I { + void m(Integer i, Bar0 b); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java.after b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java.after new file mode 100644 index 000000000000..208b9fe174ab --- /dev/null +++ b/java/java-tests/testData/refactoring/convertToInstance8Method/MethodReferenceToLambda.java.after @@ -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); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstance8MethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstance8MethodTest.java index 138d727abd05..00913c2fc5fd 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstance8MethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/convertToInstanceMethod/ConvertToInstance8MethodTest.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index 2357f39a8457..0c111efe1a92 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -584,6 +584,17 @@ public abstract class BaseRefactoringProcessor implements Runnable { return myTestIgnore; } + @TestOnly + public static void withIgnoredConflicts(ThrowableRunnable r) throws T { + try { + myTestIgnore = true; + r.run(); + } + finally { + myTestIgnore = false; + } + } + @NotNull public Collection getMessages() { List result = new ArrayList<>(messages);