inline method to method reference: convert to lambda (IDEA-148481)
authorAnna Kozlova <anna.kozlova@jetbrains.com>
Mon, 11 Jan 2016 17:49:26 +0000 (18:49 +0100)
committerAnna Kozlova <anna.kozlova@jetbrains.com>
Mon, 11 Jan 2016 17:53:47 +0000 (18:53 +0100)
12 files changed:
java/java-impl/src/com/intellij/refactoring/inline/InlineMethodHandler.java
java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java
java/java-impl/src/com/intellij/refactoring/util/LambdaRefactoringUtil.java [new file with mode: 0644]
java/java-psi-api/src/com/intellij/psi/LambdaUtil.java
java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java.after [new file with mode: 0644]
java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java.after [new file with mode: 0644]
java/java-tests/testData/refactoring/inlineMethod/SideEffectsInMethodRefQualifier.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/refactoring/inline/InlineMethodTest.java
java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/FluentIterableConversionUtil.java
plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java

index 4aeec6d791a57cc96fee9ef40a255266028ea27a..a086d22b565e2c6cea4379c969d678aa288be9aa 100644 (file)
@@ -83,11 +83,6 @@ class InlineMethodHandler extends JavaInlineActionHandler {
       return;
     }
 
       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) {
     if (reference != null) {
       final String errorMessage = InlineMethodProcessor.checkCalledInSuperOrThisExpr(methodBody, reference.getElement());
       if (errorMessage != null) {
index b2c82e29a2bbae92e0f86f5ca214e57053115b77..873ff1427fce7ec9c6718aef212880ace3b3ab1d 100644 (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.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;
 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 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);
         }
 
         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)) {
     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];
           }
         }
         else {
           myReference = addBracesWhenNeeded(new PsiReferenceExpression[]{(PsiReferenceExpression)myReference})[0];
-          inlineMethodCall((PsiReferenceExpression)myReference);
+          if (myReference instanceof PsiMethodReferenceExpression) {
+            inlineMethodReference((PsiMethodReferenceExpression)myReference);
+          }
+          else {
+            inlineMethodCall((PsiReferenceExpression)myReference);
+          }
         }
       }
       else {
         }
       }
       else {
@@ -427,7 +445,10 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
         if (myMethod.isConstructor()) {
           for (UsageInfo usage : usages) {
             PsiElement element = usage.getElement();
         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);
               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);
             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
               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) {
           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()) {
           }
           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);
   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) {
     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;
       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) {
 
       PsiElement parentStatement = RefactoringUtil.getParentStatement(ref, true);
       if (parentStatement != null) {
diff --git a/java/java-impl/src/com/intellij/refactoring/util/LambdaRefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/LambdaRefactoringUtil.java
new file mode 100644 (file)
index 0000000..b3b218b
--- /dev/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);
+          }
+        }
+      }
+    }
+  }
+}
index 7e07862810669e2280a2f8edc6fd75fea3ebe4c1..5872b4f76d96c570a3dc5d2536fbee65445090e6 100644 (file)
@@ -520,7 +520,8 @@ public class LambdaUtil {
         }
         else if (statements[0] instanceof PsiExpressionStatement) {
           expression = ((PsiExpressionStatement)statements[0]).getExpression();
         }
         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());
         }
       }
           return extractSingleExpressionFromBody(((PsiBlockStatement)statements[0]).getCodeBlock());
         }
       }
diff --git a/java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java b/java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java
new file mode 100644 (file)
index 0000000..c522b20
--- /dev/null
@@ -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;
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java.after b/java/java-tests/testData/refactoring/inlineMethod/InlineIntoConstructorRef.java.after
new file mode 100644 (file)
index 0000000..34fbae2
--- /dev/null
@@ -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);
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java b/java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java
new file mode 100644 (file)
index 0000000..762b0f5
--- /dev/null
@@ -0,0 +1,11 @@
+import java.util.function.Supplier;
+
+class Test {
+  {
+    Supplier<String> sup = this::g<caret>et;
+  }
+
+  private String get() {
+    return null;
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java.after b/java/java-tests/testData/refactoring/inlineMethod/InlineIntoMethodRef.java.after
new file mode 100644 (file)
index 0000000..2a7e11c
--- /dev/null
@@ -0,0 +1,11 @@
+import java.util.function.Supplier;
+
+class Test {
+  {
+    Supplier<String> sup = () -> null;
+  }
+
+  private String get() {
+    return null;
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inlineMethod/SideEffectsInMethodRefQualifier.java b/java/java-tests/testData/refactoring/inlineMethod/SideEffectsInMethodRefQualifier.java
new file mode 100644 (file)
index 0000000..a138b39
--- /dev/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;
+  }
+}
\ No newline at end of file
index 5b1e5a733c7b4dcb6e74f64ac312ed25ec423b8c..ba6395fa993b0aea8fbc3324b1ea40e5a0595d10 100644 (file)
@@ -292,6 +292,18 @@ public class InlineMethodTest extends LightRefactoringTestCase {
     doTestInlineThisOnly();
   }
 
     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);
   private void doTestInlineThisOnly() {
     @NonNls String fileName = "/refactoring/inlineMethod/" + getTestName(false) + ".java";
     configureByFile(fileName);
index d986896c3b8d0503bc8f217a2ca478a4763596ce..3987df0e2f26736cef1035ff479acae20e408583 100644 (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.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.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;
 
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -157,7 +156,7 @@ public class FluentIterableConversionUtil {
       }
 
       if (argument instanceof PsiMethodReferenceExpression) {
       }
 
       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>>();
       }
       if (argument instanceof PsiLambdaExpression) {
         List<Pair<PsiExpression, Boolean>> iterableReturnValues = new SmartList<Pair<PsiExpression, Boolean>>();
index ab167af90564e5654117cb6e0a7806223004150f..7a81831a0c9fb55fbef6bac5656e94936be2ad08 100644 (file)
  */
 package com.siyeh.ipp.types;
 
  */
 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.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.*;
-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.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 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());
 
 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);
   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() {
     final Runnable runnable = new Runnable() {
       public void run() {
-        introduceQualifierAsLocalVariable(editor, expr);
+        LambdaRefactoringUtil.removeSideEffectsFromLambdaBody(editor, expr);
       }
     };
     final Application application = ApplicationManager.getApplication();
       }
     };
     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) {
   private static class MethodRefPredicate implements PsiElementPredicate {
     @Override
     public boolean satisfiedBy(PsiElement element) {