inline method to method reference: convert to lambda (IDEA-148481)

This commit is contained in:
Anna Kozlova
2016-01-11 18:49:26 +01:00
parent 53c8b26ae7
commit d8ea1bc2fe
12 changed files with 406 additions and 253 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,11 @@
import java.util.function.Supplier;
class Test {
{
Supplier<String> sup = this::g<caret>et;
}
private String get() {
return null;
}
}

View File

@@ -0,0 +1,11 @@
import java.util.function.Supplier;
class Test {
{
Supplier<String> sup = () -> null;
}
private String get() {
return null;
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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>>();

View File

@@ -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) {