Bug 577699 - [extract method] Entire lambda body cannot be extracted

- fix ExtractMethodAnalyzer to not invalidate the body of a Lambda
- fix ExtractMethodRefactoring to recognize when it has a return
  or call that is replacing a lambda body and not add semi-colon
- also fix ExtractMethodRefactoring to recognize when it is creating
  a method from a Block and to unwrap the block to just move over
  the statements
- add new tests to ExtractMethodTests1d8

Change-Id: Iacacee7fa582fbcf76ceb9063faf6bf9e764f4b9
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.ui/+/188710
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Tested-by: Jeff Johnston <jjohnstn@redhat.com>
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/error_out/A_test804.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/error_out/A_test804.java
index 8e3fe00..dee7cd2 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/error_out/A_test804.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/error_out/A_test804.java
@@ -10,8 +10,6 @@
 	}
 
 	protected void extracted() {
-		/*[*/{
-			System.out.println("Hello world");
-		}/*]*/
+		System.out.println("Hello world");
 	}
 }
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test328.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test328.java
new file mode 100644
index 0000000..900a553
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test328.java
@@ -0,0 +1,16 @@
+package lambdaExpression18_in;
+
+@FunctionalInterface
+interface FI {
+	int foo1(int a);
+}
+
+class FI_1 {
+	void fun(int a) {
+		FI i1 = x1-> x1;
+		FI i2 = xxx-> /*]*/{
+			i1.foo1(a);
+			return xxx;
+		}/*[*/;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test329.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test329.java
new file mode 100644
index 0000000..8cfc43e
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test329.java
@@ -0,0 +1,17 @@
+package lambdaExpression18_in;
+
+@FunctionalInterface
+interface FI {
+	void foo1(int a);
+}
+
+class FI_1 {
+	void foo(int a, FI fi) {
+		fi.foo1(a);
+	}
+	void fun(int a) {
+		foo(a, xxx -> /*]*/{ 
+			System.out.println(a);
+		}/*[*/);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test328.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test328.java
new file mode 100644
index 0000000..27aeb33
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test328.java
@@ -0,0 +1,18 @@
+package lambdaExpression18_in;
+
+@FunctionalInterface
+interface FI {
+	int foo1(int a);
+}
+
+class FI_1 {
+	void fun(int a) {
+		FI i1 = x1-> x1;
+		FI i2 = xxx-> /*]*/extracted(a, i1, xxx)/*[*/;
+	}
+
+	private int extracted(int a, FI i1, int xxx) {
+		i1.foo1(a);
+		return xxx;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test329.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test329.java
new file mode 100644
index 0000000..c07d191
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test329.java
@@ -0,0 +1,18 @@
+package lambdaExpression18_in;
+
+@FunctionalInterface
+interface FI {
+	void foo1(int a);
+}
+
+class FI_1 {
+	void foo(int a, FI fi) {
+		fi.foo1(a);
+	}
+	void fun(int a) {
+		foo(a, xxx -> /*]*/extracted(a)/*[*/);
+	}
+	private void extracted(int a) {
+		System.out.println(a);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/semicolon_out/A_test402.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/semicolon_out/A_test402.java
index 9172545..f57be17 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/semicolon_out/A_test402.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/semicolon_out/A_test402.java
@@ -6,8 +6,6 @@
 	}
 
 	protected void extracted() {
-		/*[*/{
-			foo();
-		}/*]*/
+		foo();
 	} 
 }
diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractMethodTests1d8.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractMethodTests1d8.java
index 88702f9..b221d98 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractMethodTests1d8.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractMethodTests1d8.java
@@ -327,4 +327,14 @@
 		lambdaExpressionTest(0, Modifier.PRIVATE);
 	}
 
+	@Test
+	public void test328() throws Exception {
+		lambdaExpressionTest(0, Modifier.PRIVATE);
+	}
+
+	@Test
+	public void test329() throws Exception {
+		lambdaExpressionTest(0, Modifier.PRIVATE);
+	}
+
 }
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodAnalyzer.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodAnalyzer.java
index 3e091a7..c6d2c27 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodAnalyzer.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodAnalyzer.java
@@ -921,7 +921,7 @@
 		int bodyExclusiveEnd= bodyStart + body.getLength();
 
 		boolean isValidSelection= false;
-		if ((body instanceof Block) && (bodyStart < selectionStart && selectionExclusiveEnd <= bodyExclusiveEnd)) {
+		if ((body instanceof Block) && (bodyStart <= selectionStart && selectionExclusiveEnd <= bodyExclusiveEnd)) {
 			// if selection is inside lambda body's block
 			isValidSelection= true;
 		} else if (body instanceof Expression) {
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java
index d63540b..d7ae6d5 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2020 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -77,6 +77,7 @@
 import org.eclipse.jdt.core.dom.Initializer;
 import org.eclipse.jdt.core.dom.Javadoc;
 import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.LambdaExpression;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.Modifier;
@@ -1243,9 +1244,25 @@
 					fAnalyzer.getReturnTypeBinding().equals(fAST.resolveWellKnownType("void")); //$NON-NLS-1$
 			if (selectedNodes.length == 1) {
 				if (!isReturnVoid) {
-					statements.insertLast(fRewriter.createMoveTarget(selectedNodes[0]), substitute);
+					if (selectedNodes[0] instanceof Block) {
+						Block block= (Block)selectedNodes[0];
+						List<Statement> blockStatements= block.statements();
+						for (Statement blockStatement : blockStatements) {
+							statements.insertLast(fRewriter.createMoveTarget(blockStatement), substitute);
+						}
+					} else {
+						statements.insertLast(fRewriter.createMoveTarget(selectedNodes[0]), substitute);
+					}
 				}
-				fRewriter.replace(selectedNodes[0], replacementNode, substitute);
+				if (selectedNodes[0].getLocationInParent() == LambdaExpression.BODY_PROPERTY) {
+					if (replacementNode instanceof ExpressionStatement) {
+						fRewriter.replace(selectedNodes[0], ((ExpressionStatement)replacementNode).getExpression(), substitute);
+					} else if (replacementNode instanceof ReturnStatement) {
+						fRewriter.replace(selectedNodes[0], ((ReturnStatement)replacementNode).getExpression(), substitute);
+					}
+				} else {
+					fRewriter.replace(selectedNodes[0], replacementNode, substitute);
+				}
 			} else if (selectedNodes.length > 1) {
 				if (isReturnVoid) {
 					fRewriter.remove(selectedNodes[selectedNodes.length - 1], substitute);