mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 13:20:53 +07:00
inline method to method reference: convert to lambda (IDEA-148481)
This commit is contained in:
@@ -83,11 +83,6 @@ class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reference instanceof PsiMethodReferenceExpression) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, REFACTORING_NAME + " cannot be applied to method references", REFACTORING_NAME, HelpID.INLINE_METHOD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (reference != null) {
|
||||
final String errorMessage = InlineMethodProcessor.checkCalledInSuperOrThisExpr(methodBody, reference.getElement());
|
||||
if (errorMessage != null) {
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -193,7 +194,14 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
conflicts.putValue(element, "Inlined method is used in javadoc");
|
||||
}
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
conflicts.putValue(element, "Inlined method is used in method reference");
|
||||
final PsiExpression qualifierExpression = ((PsiMethodReferenceExpression)element).getQualifierExpression();
|
||||
if (qualifierExpression != null) {
|
||||
final List<PsiElement> sideEffects = new ArrayList<PsiElement>();
|
||||
SideEffectChecker.checkSideEffects(qualifierExpression, sideEffects);
|
||||
if (!sideEffects.isEmpty()) {
|
||||
conflicts.putValue(element, "Inlined method is used in method reference with side effects in qualifier");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String errorMessage = checkCalledInSuperOrThisExpr(myMethod.getBody(), element);
|
||||
@@ -412,14 +420,24 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
try {
|
||||
if (myInlineThisOnly) {
|
||||
if (myMethod.isConstructor() && InlineMethodHandler.isChainingConstructor(myMethod)) {
|
||||
PsiCall constructorCall = RefactoringUtil.getEnclosingConstructorCall(myReference);
|
||||
if (constructorCall != null) {
|
||||
inlineConstructorCall(constructorCall);
|
||||
if (myReference instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)myReference);
|
||||
}
|
||||
else {
|
||||
PsiCall constructorCall = RefactoringUtil.getEnclosingConstructorCall(myReference);
|
||||
if (constructorCall != null) {
|
||||
inlineConstructorCall(constructorCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
myReference = addBracesWhenNeeded(new PsiReferenceExpression[]{(PsiReferenceExpression)myReference})[0];
|
||||
inlineMethodCall((PsiReferenceExpression)myReference);
|
||||
if (myReference instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)myReference);
|
||||
}
|
||||
else {
|
||||
inlineMethodCall((PsiReferenceExpression)myReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -427,7 +445,10 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
if (myMethod.isConstructor()) {
|
||||
for (UsageInfo usage : usages) {
|
||||
PsiElement element = usage.getElement();
|
||||
if (element instanceof PsiJavaCodeReferenceElement) {
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)element);
|
||||
}
|
||||
else if (element instanceof PsiJavaCodeReferenceElement) {
|
||||
PsiCall constructorCall = RefactoringUtil.getEnclosingConstructorCall((PsiJavaCodeReferenceElement)element);
|
||||
if (constructorCall != null) {
|
||||
inlineConstructorCall(constructorCall);
|
||||
@@ -449,7 +470,8 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
final PsiElement element = usage.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
refExprList.add((PsiReferenceExpression)element);
|
||||
} else if (element instanceof PsiImportStaticReferenceElement) {
|
||||
}
|
||||
else if (element instanceof PsiImportStaticReferenceElement) {
|
||||
final JavaResolveResult[] resolveResults = ((PsiImportStaticReferenceElement)element).multiResolve(false);
|
||||
if (resolveResults.length < 2) {
|
||||
//no overloads available: ensure broken import are deleted and
|
||||
@@ -464,8 +486,12 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
PsiReferenceExpression[] refs = refExprList.toArray(new PsiReferenceExpression[refExprList.size()]);
|
||||
refs = addBracesWhenNeeded(refs);
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (ref instanceof PsiMethodReferenceExpression) continue;
|
||||
inlineMethodCall(ref);
|
||||
if (ref instanceof PsiMethodReferenceExpression) {
|
||||
inlineMethodReference((PsiMethodReferenceExpression)ref);
|
||||
}
|
||||
else {
|
||||
inlineMethodCall(ref);
|
||||
}
|
||||
}
|
||||
for (PsiElement psiElement : imports2Delete) {
|
||||
if (psiElement != null && psiElement.isValid()) {
|
||||
@@ -482,6 +508,25 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private void inlineMethodReference(PsiMethodReferenceExpression reference) {
|
||||
final PsiLambdaExpression lambdaExpression = LambdaRefactoringUtil.convertMethodReferenceToLambda(reference, false, false);
|
||||
final PsiExpression callExpression = LambdaUtil.extractSingleExpressionFromBody(lambdaExpression.getBody());
|
||||
if (callExpression instanceof PsiMethodCallExpression) {
|
||||
inlineMethodCall(((PsiMethodCallExpression)callExpression).getMethodExpression());
|
||||
}
|
||||
else if (callExpression instanceof PsiCall) {
|
||||
inlineConstructorCall((PsiCall)callExpression);
|
||||
}
|
||||
else {
|
||||
LOG.error("Unexpected expr: " + callExpression.getText());
|
||||
}
|
||||
LambdaRefactoringUtil.simplifyToExpressionLambda(lambdaExpression);
|
||||
|
||||
if (myInlineThisOnly) {
|
||||
LambdaRefactoringUtil.removeSideEffectsFromLambdaBody(myEditor, lambdaExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public static void inlineConstructorCall(PsiCall constructorCall) {
|
||||
final PsiMethod oldConstructor = constructorCall.resolveMethod();
|
||||
LOG.assertTrue(oldConstructor != null);
|
||||
@@ -1234,12 +1279,17 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
myAddedClassInitializers = new HashMap<PsiField, PsiClassInitializer>();
|
||||
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (ref instanceof PsiMethodReferenceExpression) continue;
|
||||
ref.putCopyableUserData(MARK_KEY, "");
|
||||
}
|
||||
|
||||
RefLoop:
|
||||
for (PsiReferenceExpression ref : refs) {
|
||||
if (!ref.isValid()) continue;
|
||||
if (ref instanceof PsiMethodReferenceExpression) {
|
||||
refsVector.add(ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
PsiElement parentStatement = RefactoringUtil.getParentStatement(ref, true);
|
||||
if (parentStatement != null) {
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.util;
|
||||
|
||||
import com.intellij.codeInspection.RedundantLambdaCodeBlockInspection;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.RedundantCastUtil;
|
||||
import com.intellij.refactoring.introduceField.ElementToWorkOn;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.text.UniqueNameGenerator;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LambdaRefactoringUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#" + LambdaRefactoringUtil.class.getName());
|
||||
|
||||
@NotNull
|
||||
public static PsiLambdaExpression convertMethodReferenceToLambda(final PsiMethodReferenceExpression referenceExpression,
|
||||
final boolean ignoreCast,
|
||||
final boolean simplifyToExpressionLambda) {
|
||||
final PsiElement resolve = referenceExpression.resolve();
|
||||
final PsiType functionalInterfaceType = referenceExpression.getFunctionalInterfaceType();
|
||||
final PsiClassType.ClassResolveResult functionalInterfaceResolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
|
||||
LOG.assertTrue(interfaceMethod != null);
|
||||
final PsiSubstitutor psiSubstitutor = LambdaUtil.getSubstitutor(interfaceMethod, functionalInterfaceResolveResult);
|
||||
final MethodSignature signature = interfaceMethod.getSignature(psiSubstitutor);
|
||||
final boolean isReceiver;
|
||||
if (resolve instanceof PsiMethod){
|
||||
final PsiMethod method = (PsiMethod)resolve;
|
||||
isReceiver = PsiMethodReferenceUtil.isResolvedBySecondSearch(referenceExpression, signature,
|
||||
method.isVarArgs(),
|
||||
method.hasModifierProperty(PsiModifier.STATIC),
|
||||
method.getParameterList().getParametersCount());
|
||||
}
|
||||
else {
|
||||
isReceiver = false;
|
||||
}
|
||||
final PsiParameter[] psiParameters = resolve instanceof PsiMethod ? ((PsiMethod)resolve).getParameterList().getParameters() : null;
|
||||
|
||||
final StringBuilder buf = new StringBuilder("(");
|
||||
LOG.assertTrue(functionalInterfaceType != null);
|
||||
buf.append(GenericsUtil.getVariableTypeByExpressionType(functionalInterfaceType).getCanonicalText()).append(")(");
|
||||
final PsiParameterList parameterList = interfaceMethod.getParameterList();
|
||||
final PsiParameter[] parameters = parameterList.getParameters();
|
||||
|
||||
final Map<PsiParameter, String> map = new HashMap<PsiParameter, String>();
|
||||
final UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
|
||||
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(referenceExpression.getProject());
|
||||
final String paramsString = StringUtil.join(parameters, new Function<PsiParameter, String>() {
|
||||
@Override
|
||||
public String fun(PsiParameter parameter) {
|
||||
final int parameterIndex = parameterList.getParameterIndex(parameter);
|
||||
String baseName;
|
||||
if (isReceiver && parameterIndex == 0) {
|
||||
final SuggestedNameInfo
|
||||
nameInfo = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, psiSubstitutor.substitute(parameter.getType()));
|
||||
baseName = nameInfo.names.length > 0 ? nameInfo.names[0] : parameter.getName();
|
||||
}
|
||||
else {
|
||||
final String initialName;
|
||||
if (psiParameters != null) {
|
||||
final int idx = parameterIndex - (isReceiver ? 1 : 0);
|
||||
initialName = psiParameters.length > 0 ? psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName()
|
||||
: parameter.getName();
|
||||
}
|
||||
else {
|
||||
initialName = parameter.getName();
|
||||
}
|
||||
baseName = codeStyleManager.variableNameToPropertyName(initialName, VariableKind.PARAMETER);
|
||||
}
|
||||
|
||||
if (baseName != null) {
|
||||
String parameterName = nameGenerator.generateUniqueName(codeStyleManager.suggestUniqueVariableName(baseName, referenceExpression, true));
|
||||
map.put(parameter, parameterName);
|
||||
return parameterName;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}, ", ");
|
||||
buf.append(paramsString);
|
||||
buf.append(") -> ");
|
||||
|
||||
|
||||
final JavaResolveResult resolveResult = referenceExpression.advancedResolve(false);
|
||||
final PsiElement resolveElement = resolveResult.getElement();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(referenceExpression.getProject());
|
||||
if (resolveElement instanceof PsiMember) {
|
||||
|
||||
buf.append("{");
|
||||
|
||||
if (!PsiType.VOID.equals(interfaceMethod.getReturnType())) {
|
||||
buf.append("return ");
|
||||
}
|
||||
final PsiElement qualifier = referenceExpression.getQualifier();
|
||||
PsiClass containingClass = null;
|
||||
if (resolveElement instanceof PsiMethod) {
|
||||
containingClass = ((PsiMember)resolveElement).getContainingClass();
|
||||
LOG.assertTrue(containingClass != null);
|
||||
} else if (resolveElement instanceof PsiClass) {
|
||||
containingClass = (PsiClass)resolveElement;
|
||||
}
|
||||
|
||||
final boolean onArrayRef =
|
||||
elementFactory.getArrayClass(PsiUtil.getLanguageLevel(referenceExpression)) == containingClass;
|
||||
|
||||
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
|
||||
if (isReceiver){
|
||||
buf.append(map.get(parameters[0])).append(".");
|
||||
} else {
|
||||
if (!(referenceNameElement instanceof PsiKeyword)) {
|
||||
if (qualifier instanceof PsiTypeElement) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = ((PsiTypeElement)qualifier).getInnermostComponentReferenceElement();
|
||||
LOG.assertTrue(referenceElement != null);
|
||||
buf.append(referenceElement.getReferenceName()).append(".");
|
||||
}
|
||||
else if (qualifier != null && !(qualifier instanceof PsiThisExpression && ((PsiThisExpression)qualifier).getQualifier() == null)) {
|
||||
buf.append(qualifier.getText()).append(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//new or method name
|
||||
buf.append(referenceExpression.getReferenceName());
|
||||
|
||||
if (referenceNameElement instanceof PsiKeyword) {
|
||||
//class name
|
||||
buf.append(" ");
|
||||
if (onArrayRef) {
|
||||
if (qualifier instanceof PsiTypeElement) {
|
||||
final PsiType type = ((PsiTypeElement)qualifier).getType();
|
||||
int dim = type.getArrayDimensions();
|
||||
buf.append(type.getDeepComponentType().getCanonicalText());
|
||||
buf.append("[");
|
||||
buf.append(map.get(parameters[0]));
|
||||
buf.append("]");
|
||||
while (--dim > 0) {
|
||||
buf.append("[]");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.append(((PsiMember)resolveElement).getName());
|
||||
|
||||
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
|
||||
LOG.assertTrue(containingClass != null);
|
||||
if (containingClass.hasTypeParameters() && !PsiUtil.isRawSubstitutor(containingClass, substitutor)) {
|
||||
buf.append("<").append(StringUtil.join(containingClass.getTypeParameters(), new Function<PsiTypeParameter, String>() {
|
||||
@Override
|
||||
public String fun(PsiTypeParameter parameter) {
|
||||
final PsiType psiType = substitutor.substitute(parameter);
|
||||
LOG.assertTrue(psiType != null);
|
||||
return psiType.getCanonicalText();
|
||||
}
|
||||
}, ", ")).append(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!onArrayRef || isReceiver) {
|
||||
//param list
|
||||
buf.append("(");
|
||||
boolean first = true;
|
||||
for (int i = isReceiver ? 1 : 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
if (!first) {
|
||||
buf.append(", ");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
buf.append(map.get(parameter));
|
||||
}
|
||||
buf.append(")");
|
||||
}
|
||||
|
||||
buf.append(";}");
|
||||
}
|
||||
|
||||
|
||||
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)referenceExpression.replace(elementFactory.createExpressionFromText(buf.toString(), referenceExpression));
|
||||
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)typeCastExpression.getOperand();
|
||||
LOG.assertTrue(lambdaExpression != null, buf.toString());
|
||||
if (RedundantCastUtil.isCastRedundant(typeCastExpression) || ignoreCast) {
|
||||
final PsiExpression operand = typeCastExpression.getOperand();
|
||||
LOG.assertTrue(operand != null);
|
||||
lambdaExpression = (PsiLambdaExpression)typeCastExpression.replace(operand);
|
||||
}
|
||||
|
||||
if (simplifyToExpressionLambda) {
|
||||
simplifyToExpressionLambda(lambdaExpression);
|
||||
}
|
||||
|
||||
return lambdaExpression;
|
||||
}
|
||||
|
||||
public static void simplifyToExpressionLambda(@NotNull final PsiLambdaExpression lambdaExpression) {
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
final PsiExpression singleExpression = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression, body);
|
||||
if (singleExpression != null) {
|
||||
body.replace(singleExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Works for expression lambdas/one statement code block lambdas to ensures equivalent method ref -> lambda transformation.
|
||||
*/
|
||||
public static void removeSideEffectsFromLambdaBody(Editor editor, PsiLambdaExpression lambdaExpression) {
|
||||
if (lambdaExpression != null && lambdaExpression.isValid()) {
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
PsiExpression methodCall = LambdaUtil.extractSingleExpressionFromBody(body);
|
||||
PsiExpression qualifierExpression = null;
|
||||
if (methodCall instanceof PsiMethodCallExpression) {
|
||||
qualifierExpression = ((PsiMethodCallExpression)methodCall).getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
else if (methodCall instanceof PsiNewExpression) {
|
||||
qualifierExpression = ((PsiNewExpression)methodCall).getQualifier();
|
||||
}
|
||||
|
||||
if (qualifierExpression != null) {
|
||||
final List<PsiElement> sideEffects = new ArrayList<PsiElement>();
|
||||
SideEffectChecker.checkSideEffects(qualifierExpression, sideEffects);
|
||||
if (!sideEffects.isEmpty()) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode() ||
|
||||
Messages.showYesNoDialog(lambdaExpression.getProject(), "There are possible side effects found in method reference qualifier." +
|
||||
"\nIntroduce local variable?", "Side Effects Detected", Messages.getQuestionIcon()) == Messages.YES) {
|
||||
//ensure introduced before lambda
|
||||
qualifierExpression.putUserData(ElementToWorkOn.PARENT, lambdaExpression);
|
||||
new IntroduceVariableHandler().invoke(qualifierExpression.getProject(), editor, qualifierExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,7 +520,8 @@ public class LambdaUtil {
|
||||
}
|
||||
else if (statements[0] instanceof PsiExpressionStatement) {
|
||||
expression = ((PsiExpressionStatement)statements[0]).getExpression();
|
||||
} else if (statements[0] instanceof PsiBlockStatement) {
|
||||
}
|
||||
else if (statements[0] instanceof PsiBlockStatement) {
|
||||
return extractSingleExpressionFromBody(((PsiBlockStatement)statements[0]).getCodeBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Test {
|
||||
|
||||
public Test() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public Test(int i) {}
|
||||
|
||||
|
||||
{
|
||||
Supplier<Test> sup = Test::ne<caret>w;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Test {
|
||||
|
||||
public Test() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public Test(int i) {}
|
||||
|
||||
|
||||
{
|
||||
Supplier<Test> sup = () -> new Test(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class Test {
|
||||
{
|
||||
Supplier<String> sup = this::g<caret>et;
|
||||
}
|
||||
|
||||
private String get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class Test {
|
||||
{
|
||||
Supplier<String> sup = () -> null;
|
||||
}
|
||||
|
||||
private String get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Test {
|
||||
|
||||
{
|
||||
Supplier<String> sup = new Test()::get;
|
||||
}
|
||||
|
||||
private String ge<caret>t() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -292,6 +292,18 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
doTestInlineThisOnly();
|
||||
}
|
||||
|
||||
public void testInlineIntoMethodRef() throws Exception {
|
||||
doTestInlineThisOnly();
|
||||
}
|
||||
|
||||
public void testInlineIntoConstructorRef() throws Exception {
|
||||
doTestInlineThisOnly();
|
||||
}
|
||||
|
||||
public void testSideEffectsInMethodRefQualifier() throws Exception {
|
||||
doTestConflict("Inlined method is used in method reference with side effects in qualifier");
|
||||
}
|
||||
|
||||
private void doTestInlineThisOnly() {
|
||||
@NonNls String fileName = "/refactoring/inlineMethod/" + getTestName(false) + ".java";
|
||||
configureByFile(fileName);
|
||||
|
||||
@@ -29,12 +29,11 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
|
||||
import com.intellij.refactoring.typeMigration.TypeEvaluator;
|
||||
import com.intellij.refactoring.util.LambdaRefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.text.UniqueNameGenerator;
|
||||
import com.siyeh.ig.psiutils.ParenthesesUtils;
|
||||
import com.siyeh.ipp.types.ReplaceMethodRefWithLambdaIntention;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -157,7 +156,7 @@ public class FluentIterableConversionUtil {
|
||||
}
|
||||
|
||||
if (argument instanceof PsiMethodReferenceExpression) {
|
||||
argument = ReplaceMethodRefWithLambdaIntention.convertMethodReferenceToLambda((PsiMethodReferenceExpression)argument, true);
|
||||
argument = LambdaRefactoringUtil.convertMethodReferenceToLambda((PsiMethodReferenceExpression)argument, true, true);
|
||||
}
|
||||
if (argument instanceof PsiLambdaExpression) {
|
||||
List<Pair<PsiExpression, Boolean>> iterableReturnValues = new SmartList<Pair<PsiExpression, Boolean>>();
|
||||
|
||||
@@ -15,34 +15,17 @@
|
||||
*/
|
||||
package com.siyeh.ipp.types;
|
||||
|
||||
import com.intellij.codeInspection.RedundantLambdaCodeBlockInspection;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.RedundantCastUtil;
|
||||
import com.intellij.refactoring.introduceField.ElementToWorkOn;
|
||||
import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.text.UniqueNameGenerator;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import com.intellij.refactoring.util.LambdaRefactoringUtil;
|
||||
import com.siyeh.ipp.base.Intention;
|
||||
import com.siyeh.ipp.base.PsiElementPredicate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReplaceMethodRefWithLambdaIntention extends Intention {
|
||||
private static final Logger LOG = Logger.getInstance("#" + ReplaceMethodRefWithLambdaIntention.class.getName());
|
||||
|
||||
@@ -59,10 +42,10 @@ public class ReplaceMethodRefWithLambdaIntention extends Intention {
|
||||
protected void processIntention(final Editor editor, @NotNull PsiElement element) {
|
||||
final PsiMethodReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class);
|
||||
LOG.assertTrue(referenceExpression != null);
|
||||
final PsiLambdaExpression expr = convertMethodReferenceToLambda(referenceExpression, false);
|
||||
final PsiLambdaExpression expr = LambdaRefactoringUtil.convertMethodReferenceToLambda(referenceExpression, false, true);
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
introduceQualifierAsLocalVariable(editor, expr);
|
||||
LambdaRefactoringUtil.removeSideEffectsFromLambdaBody(editor, expr);
|
||||
}
|
||||
};
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
@@ -73,221 +56,6 @@ public class ReplaceMethodRefWithLambdaIntention extends Intention {
|
||||
}
|
||||
}
|
||||
|
||||
public static PsiLambdaExpression convertMethodReferenceToLambda(final PsiMethodReferenceExpression referenceExpression, final boolean ignoreCast) {
|
||||
final PsiElement resolve = referenceExpression.resolve();
|
||||
final PsiType functionalInterfaceType = referenceExpression.getFunctionalInterfaceType();
|
||||
final PsiClassType.ClassResolveResult functionalInterfaceResolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
|
||||
LOG.assertTrue(interfaceMethod != null);
|
||||
final PsiSubstitutor psiSubstitutor = LambdaUtil.getSubstitutor(interfaceMethod, functionalInterfaceResolveResult);
|
||||
final MethodSignature signature = interfaceMethod.getSignature(psiSubstitutor);
|
||||
final boolean isReceiver;
|
||||
if (resolve instanceof PsiMethod){
|
||||
final PsiMethod method = (PsiMethod)resolve;
|
||||
isReceiver = PsiMethodReferenceUtil.isResolvedBySecondSearch(referenceExpression, signature,
|
||||
method.isVarArgs(),
|
||||
method.hasModifierProperty(PsiModifier.STATIC),
|
||||
method.getParameterList().getParametersCount());
|
||||
}
|
||||
else {
|
||||
isReceiver = false;
|
||||
}
|
||||
final PsiParameter[] psiParameters = resolve instanceof PsiMethod ? ((PsiMethod)resolve).getParameterList().getParameters() : null;
|
||||
|
||||
final StringBuilder buf = new StringBuilder("(");
|
||||
LOG.assertTrue(functionalInterfaceType != null);
|
||||
buf.append(GenericsUtil.getVariableTypeByExpressionType(functionalInterfaceType).getCanonicalText()).append(")(");
|
||||
final PsiParameterList parameterList = interfaceMethod.getParameterList();
|
||||
final PsiParameter[] parameters = parameterList.getParameters();
|
||||
|
||||
final Map<PsiParameter, String> map = new HashMap<PsiParameter, String>();
|
||||
final UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
|
||||
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(referenceExpression.getProject());
|
||||
final String paramsString = StringUtil.join(parameters, new Function<PsiParameter, String>() {
|
||||
@Override
|
||||
public String fun(PsiParameter parameter) {
|
||||
final int parameterIndex = parameterList.getParameterIndex(parameter);
|
||||
String baseName;
|
||||
if (isReceiver && parameterIndex == 0) {
|
||||
final SuggestedNameInfo
|
||||
nameInfo = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, psiSubstitutor.substitute(parameter.getType()));
|
||||
baseName = nameInfo.names.length > 0 ? nameInfo.names[0] : parameter.getName();
|
||||
}
|
||||
else {
|
||||
final String initialName;
|
||||
if (psiParameters != null) {
|
||||
final int idx = parameterIndex - (isReceiver ? 1 : 0);
|
||||
initialName = psiParameters.length > 0 ? psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName()
|
||||
: parameter.getName();
|
||||
}
|
||||
else {
|
||||
initialName = parameter.getName();
|
||||
}
|
||||
baseName = codeStyleManager.variableNameToPropertyName(initialName, VariableKind.PARAMETER);
|
||||
}
|
||||
|
||||
if (baseName != null) {
|
||||
String parameterName = nameGenerator.generateUniqueName(codeStyleManager.suggestUniqueVariableName(baseName, referenceExpression, true));
|
||||
map.put(parameter, parameterName);
|
||||
return parameterName;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}, ", ");
|
||||
buf.append(paramsString);
|
||||
buf.append(") -> ");
|
||||
|
||||
|
||||
final JavaResolveResult resolveResult = referenceExpression.advancedResolve(false);
|
||||
final PsiElement resolveElement = resolveResult.getElement();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(referenceExpression.getProject());
|
||||
if (resolveElement instanceof PsiMember) {
|
||||
|
||||
buf.append("{");
|
||||
|
||||
if (!PsiType.VOID.equals(interfaceMethod.getReturnType())) {
|
||||
buf.append("return ");
|
||||
}
|
||||
final PsiElement qualifier = referenceExpression.getQualifier();
|
||||
PsiClass containingClass = null;
|
||||
if (resolveElement instanceof PsiMethod) {
|
||||
containingClass = ((PsiMember)resolveElement).getContainingClass();
|
||||
LOG.assertTrue(containingClass != null);
|
||||
} else if (resolveElement instanceof PsiClass) {
|
||||
containingClass = (PsiClass)resolveElement;
|
||||
}
|
||||
|
||||
final boolean onArrayRef =
|
||||
elementFactory.getArrayClass(PsiUtil.getLanguageLevel(referenceExpression)) == containingClass;
|
||||
|
||||
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
|
||||
if (isReceiver){
|
||||
buf.append(map.get(parameters[0])).append(".");
|
||||
} else {
|
||||
if (!(referenceNameElement instanceof PsiKeyword)) {
|
||||
if (qualifier instanceof PsiTypeElement) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = ((PsiTypeElement)qualifier).getInnermostComponentReferenceElement();
|
||||
LOG.assertTrue(referenceElement != null);
|
||||
buf.append(referenceElement.getReferenceName()).append(".");
|
||||
}
|
||||
else if (qualifier != null && !(qualifier instanceof PsiThisExpression && ((PsiThisExpression)qualifier).getQualifier() == null)) {
|
||||
buf.append(qualifier.getText()).append(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//new or method name
|
||||
buf.append(referenceExpression.getReferenceName());
|
||||
|
||||
if (referenceNameElement instanceof PsiKeyword) {
|
||||
//class name
|
||||
buf.append(" ");
|
||||
if (onArrayRef) {
|
||||
if (qualifier instanceof PsiTypeElement) {
|
||||
final PsiType type = ((PsiTypeElement)qualifier).getType();
|
||||
int dim = type.getArrayDimensions();
|
||||
buf.append(type.getDeepComponentType().getCanonicalText());
|
||||
buf.append("[");
|
||||
buf.append(map.get(parameters[0]));
|
||||
buf.append("]");
|
||||
while (--dim > 0) {
|
||||
buf.append("[]");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.append(((PsiMember)resolveElement).getName());
|
||||
|
||||
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
|
||||
LOG.assertTrue(containingClass != null);
|
||||
if (containingClass.hasTypeParameters() && !PsiUtil.isRawSubstitutor(containingClass, substitutor)) {
|
||||
buf.append("<").append(StringUtil.join(containingClass.getTypeParameters(), new Function<PsiTypeParameter, String>() {
|
||||
@Override
|
||||
public String fun(PsiTypeParameter parameter) {
|
||||
final PsiType psiType = substitutor.substitute(parameter);
|
||||
LOG.assertTrue(psiType != null);
|
||||
return psiType.getCanonicalText();
|
||||
}
|
||||
}, ", ")).append(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!onArrayRef || isReceiver) {
|
||||
//param list
|
||||
buf.append("(");
|
||||
boolean first = true;
|
||||
for (int i = isReceiver ? 1 : 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
if (!first) {
|
||||
buf.append(", ");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
buf.append(map.get(parameter));
|
||||
}
|
||||
buf.append(")");
|
||||
}
|
||||
|
||||
buf.append(";}");
|
||||
}
|
||||
|
||||
|
||||
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)referenceExpression.replace(elementFactory.createExpressionFromText(buf.toString(), referenceExpression));
|
||||
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)typeCastExpression.getOperand();
|
||||
LOG.assertTrue(lambdaExpression != null, buf.toString());
|
||||
if (RedundantCastUtil.isCastRedundant(typeCastExpression) || ignoreCast) {
|
||||
final PsiExpression operand = typeCastExpression.getOperand();
|
||||
LOG.assertTrue(operand != null);
|
||||
lambdaExpression = (PsiLambdaExpression)typeCastExpression.replace(operand);
|
||||
}
|
||||
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
final PsiExpression singleExpression = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression, body);
|
||||
if (singleExpression != null) {
|
||||
body.replace(singleExpression);
|
||||
}
|
||||
|
||||
return lambdaExpression;
|
||||
}
|
||||
|
||||
private static void introduceQualifierAsLocalVariable(Editor editor, PsiLambdaExpression lambdaExpression) {
|
||||
if (lambdaExpression != null && lambdaExpression.isValid()) {
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
PsiExpression methodCall = null;
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements();
|
||||
LOG.assertTrue(statements.length == 1);
|
||||
final PsiStatement statement = statements[0];
|
||||
if (statement instanceof PsiReturnStatement) {
|
||||
methodCall = ((PsiReturnStatement)statement).getReturnValue();
|
||||
}
|
||||
else if (statement instanceof PsiExpressionStatement) {
|
||||
methodCall = ((PsiExpressionStatement)statement).getExpression();
|
||||
}
|
||||
} else {
|
||||
methodCall = (PsiExpression)body;
|
||||
}
|
||||
PsiExpression qualifierExpression = null;
|
||||
if (methodCall instanceof PsiMethodCallExpression) {
|
||||
qualifierExpression = ((PsiMethodCallExpression)methodCall).getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
else if (methodCall instanceof PsiNewExpression) {
|
||||
qualifierExpression = ((PsiNewExpression)methodCall).getQualifier();
|
||||
}
|
||||
|
||||
if (qualifierExpression != null) {
|
||||
final List<PsiElement> sideEffects = new ArrayList<PsiElement>();
|
||||
SideEffectChecker.checkSideEffects(qualifierExpression, sideEffects);
|
||||
if (!sideEffects.isEmpty()) {
|
||||
//ensure introduced before lambda
|
||||
qualifierExpression.putUserData(ElementToWorkOn.PARENT, lambdaExpression);
|
||||
new IntroduceVariableHandler().invoke(qualifierExpression.getProject(), editor, qualifierExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MethodRefPredicate implements PsiElementPredicate {
|
||||
@Override
|
||||
public boolean satisfiedBy(PsiElement element) {
|
||||
|
||||
Reference in New Issue
Block a user