Bug 550394 - [cleanup & saveaction] Simplify lambda expressions

Change-Id: I6045a8772bb845875525a86dc8fa67eca506f9af
Signed-off-by: Fabrice-TIERCELIN <fabrice.tiercelin@yahoo.fr>
Depends-On: I2829d8ed4a876ae6a9b63cee2834bbd611318ab0
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java
index 972bfb1..42efac6 100644
--- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -104,6 +104,7 @@
 	public static String CodeFormatFix_description;
 	public static String LambdaExpressionsCleanUp_use_lambda_where_possible;
 	public static String LambdaExpressionsCleanUp_use_anonymous;
+	public static String LambdaExpressionAndMethodRefCleanUp_description;
 
 	public static String NullAnnotationsCleanUp_add_nullable_annotation;
 	public static String NullAnnotationsCleanUp_add_nonnull_annotation;
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties
index 73f7320..9041e0e 100644
--- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/MultiFixMessages.properties
@@ -84,6 +84,7 @@
 ImportsCleanUp_OrganizeImports_Description=Organize imports
 LambdaExpressionsCleanUp_use_lambda_where_possible=Use lambda where possible
 LambdaExpressionsCleanUp_use_anonymous=Use anonymous class creations
+LambdaExpressionAndMethodRefCleanUp_description=Simplify lambda expression and method reference syntax
 SortMembersCleanUp_AllMembers_description=Sort all members
 SortMembersCleanUp_Excluding_description=Sort members excluding fields, enum constants, and initializers
 SortMembersCleanUp_RemoveMarkersWarning0=The file ''{0}'' in project ''{1}'' contains markers which may be removed by Sort Members
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
index 718e45f..a0523b8 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodeFactory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Red Hat Inc. - refactored to jdt.core.manipulation
+ *     Fabrice TIERCELIN - Methods to identify a signature
  *******************************************************************************/
 package org.eclipse.jdt.internal.corext.dom;
 
@@ -38,6 +39,7 @@
 import org.eclipse.jdt.core.dom.Name;
 import org.eclipse.jdt.core.dom.NodeFinder;
 import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
 import org.eclipse.jdt.core.dom.PrimitiveType;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.Type;
@@ -55,7 +57,7 @@
 /**
  * JDT-UI-internal helper methods to create new {@link ASTNode}s.
  * Complements <code>AST#new*(..)</code> and <code>ImportRewrite#add*(..)</code>.
- * 
+ *
  * see JDTUIHelperClasses
  */
 public class ASTNodeFactory {
@@ -86,6 +88,41 @@
 		// no instance;
 	}
 
+	/**
+	 * Parenthesizes the provided expression if its type requires it.
+	 *
+	 * @param ast The AST to create the resulting node with.
+	 * @param expression the expression to conditionally return parenthesized
+	 * @return the parenthesized expression of the provided expression to return or this expression
+	 *         itself
+	 */
+	public static Expression parenthesizeIfNeeded(AST ast, Expression expression) {
+		switch (expression.getNodeType()) {
+			case ASTNode.ASSIGNMENT:
+			case ASTNode.CAST_EXPRESSION:
+			case ASTNode.CONDITIONAL_EXPRESSION:
+			case ASTNode.INFIX_EXPRESSION:
+			case ASTNode.INSTANCEOF_EXPRESSION:
+				return parenthesize(ast, expression);
+
+			default:
+				return expression;
+		}
+	}
+
+	/**
+	 * Builds a new {@link ParenthesizedExpression} instance.
+	 *
+	 * @param ast The AST to create the resulting node with.
+	 * @param expression the expression to wrap with parentheses
+	 * @return a new parenthesized expression
+	 */
+	public static ParenthesizedExpression parenthesize(AST ast, Expression expression) {
+		final ParenthesizedExpression pe= ast.newParenthesizedExpression();
+		pe.setExpression(expression);
+		return pe;
+	}
+
 	public static ASTNode newStatement(AST ast, String content) {
 		StringBuilder buffer= new StringBuilder(STATEMENT_HEADER);
 		buffer.append(content);
@@ -139,7 +176,7 @@
 	 * Returns an {@link ArrayType} that adds one dimension to the given type node.
 	 * If the given node is already an ArrayType, then a new {@link Dimension}
 	 * without annotations is inserted at the first position.
-	 * 
+	 *
 	 * @param type the type to be wrapped
 	 * @return the array type
 	 * @since 3.10
@@ -170,13 +207,13 @@
 	 * Returns the new type node corresponding to the type of the given declaration
 	 * including the extra dimensions. If the type is a {@link UnionType}, use the LUB type.
 	 * If the <code>importRewrite</code> is <code>null</code>, the type may be fully-qualified.
-	 * 
+	 *
 	 * @param ast The AST to create the resulting type with.
 	 * @param declaration The variable declaration to get the type from
 	 * @param importRewrite the import rewrite to use, or <code>null</code>
 	 * @param context the import rewrite context, or <code>null</code>
 	 * @return a new type node created with the given AST.
-	 * 
+	 *
 	 * @since 3.7.1
 	 */
 	public static Type newType(AST ast, VariableDeclaration declaration, ImportRewrite importRewrite, ImportRewriteContext context) {
@@ -206,9 +243,9 @@
 				return type;
 			}
 		}
-		
+
 		type= (Type) ASTNode.copySubtree(ast, type);
-		
+
 		List<Dimension> extraDimensions= declaration.extraDimensions();
 		if (!extraDimensions.isEmpty()) {
 			ArrayType arrayType;
@@ -281,14 +318,14 @@
 	/**
 	 * Returns the new type node representing the return type of <code>lambdaExpression</code>
 	 * including the extra dimensions.
-	 * 
+	 *
 	 * @param lambdaExpression the lambda expression
 	 * @param ast the AST to create the return type with
 	 * @param importRewrite the import rewrite to use, or <code>null</code>
 	 * @param context the import rewrite context, or <code>null</code>
 	 * @return a new type node created with the given AST representing the return type of
 	 *         <code>lambdaExpression</code>
-	 * 
+	 *
 	 * @since 3.10
 	 */
 	public static Type newReturnType(LambdaExpression lambdaExpression, AST ast, ImportRewrite importRewrite, ImportRewriteContext context) {
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
index 5e4e0a6..689691a 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -418,6 +418,24 @@
 		return Modifier.isStatic(declaration.getModifiers());
 	}
 
+	/**
+	 * True if the method is static, false if it is not or null if it is unknown.
+	 *
+	 * @param method The method
+	 * @return True if the method is static, false if it is not or null if it is unknown.
+	 */
+	public static Boolean isStatic(final MethodInvocation method) {
+		Expression calledType= method.getExpression();
+
+		if (method.resolveMethodBinding() != null) {
+			return (method.resolveMethodBinding().getModifiers() & Modifier.STATIC) != 0;
+		} else if ((calledType instanceof Name) && ((Name) calledType).resolveBinding().getKind() == IBinding.TYPE) {
+			return Boolean.TRUE;
+		}
+
+		return null;
+	}
+
 	public static List<BodyDeclaration> getBodyDeclarations(ASTNode node) {
 		if (node instanceof AbstractTypeDeclaration) {
 			return ((AbstractTypeDeclaration)node).bodyDeclarations();
@@ -1302,6 +1320,35 @@
 		return current;
 	}
 
+    /**
+     * Returns the same node after removing any parentheses around it.
+     *
+     * @param node the node around which parentheses must be removed
+     * @return the same node after removing any parentheses around it. If there are
+     *         no parentheses around it then the exact same node is returned
+     */
+    public static ASTNode getUnparenthesedExpression(ASTNode node) {
+        if (node instanceof Expression) {
+            return getUnparenthesedExpression((Expression) node);
+        }
+        return node;
+    }
+
+    /**
+     * Returns the same expression after removing any parentheses around it.
+     *
+     * @param expression the expression around which parentheses must be removed
+     * @return the same expression after removing any parentheses around it If there
+     *         are no parentheses around it then the exact same expression is
+     *         returned
+     */
+    public static Expression getUnparenthesedExpression(Expression expression) {
+		if (expression != null && expression.getNodeType() == ASTNode.PARENTHESIZED_EXPRESSION) {
+            return getUnparenthesedExpression(((ParenthesizedExpression) expression).getExpression());
+        }
+        return expression;
+    }
+
 	/**
 	 * Returns <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code>
 	 * (i.e. returns <code>false</code> if <code>parent == node</code>).
@@ -1577,6 +1624,7 @@
 			return ((SimpleName) name).isDeclaration();
 		}
 	}
+
     /**
      * Returns whether the provided method invocation invokes a method with the
      * provided method signature. The method signature is compared against the
@@ -1608,7 +1656,7 @@
 	 * @return true if the provided method invocation matches the provided method signature, false
 	 *         otherwise
 	 */
-	private static boolean usesGivenSignature(final IMethodBinding methodBinding, final String typeQualifiedName, final String methodName,
+	public static boolean usesGivenSignature(final IMethodBinding methodBinding, final String typeQualifiedName, final String methodName,
 			final String... parameterTypesQualifiedNames) {
 		// Let's do the fast checks first
 		if (methodBinding == null || !methodName.equals(methodBinding.getName())
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java
index 8757d89..f2a17dc 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -679,6 +679,19 @@
 	public static final String USE_ANONYMOUS_CLASS_CREATION= "cleanup.use_anonymous_class_creation"; //$NON-NLS-1$
 
 	/**
+	 * Removes useless parenthesis, return statements and brackets from lambda expressions and
+	 * method references.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.15
+	 */
+	public static final String SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF= "cleanup.simplify_lambda_expression_and_method_ref"; //$NON-NLS-1$
+
+	/**
 	 * Adds type parameters to raw type references.
 	 * <p>
 	 * Example:
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
index ca50a29..769636b 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest18.java
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Fabrice TIERCELIN - Lambda expression cleanup
  *******************************************************************************/
 package org.eclipse.jdt.ui.tests.quickfix;
 
@@ -57,6 +58,268 @@
 	}
 
 	@Test
+	public void testSimplifyLambdaExpression() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import static java.util.Calendar.getInstance;\n" //
+				+ "import static java.util.Calendar.getAvailableLocales;\n" //
+				+ "\n" //
+				+ "import java.time.Instant;\n" //
+				+ "import java.util.ArrayList;\n" //
+				+ "import java.util.Calendar;\n" //
+				+ "import java.util.Date;\n" //
+				+ "import java.util.Locale;\n" //
+				+ "import java.util.Vector;\n" //
+				+ "import java.util.function.BiFunction;\n" //
+				+ "import java.util.function.Function;\n" //
+				+ "import java.util.function.Supplier;\n" //
+				+ "\n" //
+				+ "public class E extends Date {\n" //
+				+ "    public String changeableText = \"foo\";\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeParentheses() {\n" //
+				+ "        return (someString) -> someString.trim().toLowerCase();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> doNotRemoveParenthesesWithSingleVariableDeclaration() {\n" //
+				+ "        return (String someString) -> someString.trim().toLowerCase();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<String, String, Integer> doNotRemoveParenthesesWithTwoParameters() {\n" //
+				+ "        return (someString, anotherString) -> someString.trim().compareTo(anotherString.trim());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Supplier<Boolean> doNotRemoveParenthesesWithNoParameter() {\n" //
+				+ "        return () -> {System.out.println(\"foo\");return true;};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeReturnAndBrackets() {\n" //
+				+ "        return someString -> {return someString.trim().toLowerCase();};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeReturnAndBracketsWithParentheses() {\n" //
+				+ "        return someString -> {return someString.trim().toLowerCase() + \"bar\";};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> doNotRemoveReturnWithSeveralStatements() {\n" //
+				+ "        return someString -> {String trimmed = someString.trim();\n" //
+				+ "        return trimmed.toLowerCase();};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Supplier<ArrayList<String>> useCreationReference() {\n" //
+				+ "        return () -> new ArrayList<>();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> useCreationReferenceWithParameter() {\n" //
+				+ "        return capacity -> new ArrayList<>(capacity);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> useCreationReferenceWithParameterAndType() {\n" //
+				+ "        // TODO this can be refactored like useCreationReferenceWithParameter\n" //
+				+ "        return (Integer capacity) -> new ArrayList<>(capacity);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> doNotRefactorWithExpressions() {\n" //
+				+ "        return capacity -> new ArrayList<>(capacity + 1);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Integer, Integer, Vector<String>> useCreationReferenceWithParameters() {\n" //
+				+ "        return (initialCapacity, capacityIncrement) -> new Vector<>(initialCapacity, capacityIncrement);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Integer, Integer, Vector<String>> doNotRefactorShuffledParams() {\n" //
+				+ "        return (initialCapacity, capacityIncrement) -> new Vector<>(capacityIncrement, initialCapacity);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Long> useMethodReference() {\n" //
+				+ "        return date -> date.getTime();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Date, Date, Integer> useMethodReferenceWithParameter() {\n" //
+				+ "        return (date, anotherDate) -> date.compareTo(anotherDate);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Long> useTypeReference() {\n" //
+				+ "        return numberInText -> Long.getLong(numberInText);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Function<Instant, Date> useTypeReferenceOnClassMethod() {\n" //
+				+ "        return instant -> Date.from(instant);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Function<Locale, Calendar> useTypeReferenceOnImportedMethod() {\n" //
+				+ "        return locale -> Calendar.getInstance(locale);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Supplier<Locale[]> useTypeReferenceAsSupplier() {\n" //
+				+ "        return () -> Calendar.getAvailableLocales();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Integer> useExpressionMethodReferenceOnLiteral() {\n" //
+				+ "        return textToSearch -> \"AutoRefactor\".indexOf(textToSearch);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Integer> doNotUseExpressionMethodReferenceOnVariable() {\n" //
+				+ "        return textToSearch -> this.changeableText.indexOf(textToSearch);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useThisMethodReference() {\n" //
+				+ "        return anotherDate -> this.compareTo(anotherDate);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useThisMethodReferenceAddThis() {\n" //
+				+ "        return anotherDate -> this.compareTo(anotherDate);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useSuperMethodReference() {\n" //
+				+ "        return anotherDate -> super.compareTo(anotherDate);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, String> doNotUseConflictingMethodReference() {\n" //
+				+ "        return numberToPrint -> numberToPrint.toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, String> doNotUseConflictingStaticMethodReference() {\n" //
+				+ "        return numberToPrint -> Integer.toString(numberToPrint);\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF);
+
+		sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import static java.util.Calendar.getInstance;\n" //
+				+ "import static java.util.Calendar.getAvailableLocales;\n" //
+				+ "\n" //
+				+ "import java.time.Instant;\n" //
+				+ "import java.util.ArrayList;\n" //
+				+ "import java.util.Calendar;\n" //
+				+ "import java.util.Date;\n" //
+				+ "import java.util.Locale;\n" //
+				+ "import java.util.Vector;\n" //
+				+ "import java.util.function.BiFunction;\n" //
+				+ "import java.util.function.Function;\n" //
+				+ "import java.util.function.Supplier;\n" //
+				+ "\n" //
+				+ "public class E extends Date {\n" //
+				+ "    public String changeableText = \"foo\";\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeParentheses() {\n" //
+				+ "        return someString -> someString.trim().toLowerCase();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> doNotRemoveParenthesesWithSingleVariableDeclaration() {\n" //
+				+ "        return (String someString) -> someString.trim().toLowerCase();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<String, String, Integer> doNotRemoveParenthesesWithTwoParameters() {\n" //
+				+ "        return (someString, anotherString) -> someString.trim().compareTo(anotherString.trim());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Supplier<Boolean> doNotRemoveParenthesesWithNoParameter() {\n" //
+				+ "        return () -> {System.out.println(\"foo\");return true;};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeReturnAndBrackets() {\n" //
+				+ "        return someString -> someString.trim().toLowerCase();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> removeReturnAndBracketsWithParentheses() {\n" //
+				+ "        return someString -> (someString.trim().toLowerCase() + \"bar\");\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, String> doNotRemoveReturnWithSeveralStatements() {\n" //
+				+ "        return someString -> {String trimmed = someString.trim();\n" //
+				+ "        return trimmed.toLowerCase();};\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Supplier<ArrayList<String>> useCreationReference() {\n" //
+				+ "        return ArrayList::new;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> useCreationReferenceWithParameter() {\n" //
+				+ "        return ArrayList::new;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> useCreationReferenceWithParameterAndType() {\n" //
+				+ "        // TODO this can be refactored like useCreationReferenceWithParameter\n" //
+				+ "        return (Integer capacity) -> new ArrayList<>(capacity);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, ArrayList<String>> doNotRefactorWithExpressions() {\n" //
+				+ "        return capacity -> new ArrayList<>(capacity + 1);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Integer, Integer, Vector<String>> useCreationReferenceWithParameters() {\n" //
+				+ "        return Vector::new;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Integer, Integer, Vector<String>> doNotRefactorShuffledParams() {\n" //
+				+ "        return (initialCapacity, capacityIncrement) -> new Vector<>(capacityIncrement, initialCapacity);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Long> useMethodReference() {\n" //
+				+ "        return Date::getTime;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public BiFunction<Date, Date, Integer> useMethodReferenceWithParameter() {\n" //
+				+ "        return Date::compareTo;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Long> useTypeReference() {\n" //
+				+ "        return Long::getLong;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Function<Instant, Date> useTypeReferenceOnClassMethod() {\n" //
+				+ "        return instant -> Date.from(instant);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Function<Locale, Calendar> useTypeReferenceOnImportedMethod() {\n" //
+				+ "        return Calendar::getInstance;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public static Supplier<Locale[]> useTypeReferenceAsSupplier() {\n" //
+				+ "        return Calendar::getAvailableLocales;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Integer> useExpressionMethodReferenceOnLiteral() {\n" //
+				+ "        return \"AutoRefactor\"::indexOf;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<String, Integer> doNotUseExpressionMethodReferenceOnVariable() {\n" //
+				+ "        return textToSearch -> this.changeableText.indexOf(textToSearch);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useThisMethodReference() {\n" //
+				+ "        return this::compareTo;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useThisMethodReferenceAddThis() {\n" //
+				+ "        return this::compareTo;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Date, Integer> useSuperMethodReference() {\n" //
+				+ "        return super::compareTo;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, String> doNotUseConflictingMethodReference() {\n" //
+				+ "        return numberToPrint -> numberToPrint.toString();\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public Function<Integer, String> doNotUseConflictingStaticMethodReference() {\n" //
+				+ "        return numberToPrint -> Integer.toString(numberToPrint);\n" //
+				+ "    }\n" //
+				+ "}\n";
+		String expected1= sample;
+
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
+	}
+
+	@Test
 	public void testConvertToLambda01() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("test", false, null);
 		StringBuffer buf= new StringBuffer();
@@ -87,10 +350,10 @@
 		String expected1= buf.toString();
 
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -136,10 +399,10 @@
 		String expected1= buf.toString();
 
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -208,10 +471,10 @@
 		buf.append("}\n");
 		String original= buf.toString();
 		ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-		
+
 		enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
 		enable(CleanUpConstants.USE_LAMBDA);
-		
+
 		buf= new StringBuffer();
 		buf.append("package test;\n");
 		buf.append("import java.util.concurrent.Executors;\n");
@@ -221,12 +484,12 @@
 		buf.append("    }\n");
 		buf.append("}\n");
 		String expected1= buf.toString();
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -373,10 +636,10 @@
 		String expected1= buf.toString();
 
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -470,10 +733,10 @@
 		buf.append("}\n");
 		String original= buf.toString();
 		ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-	
+
 		enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
 		enable(CleanUpConstants.USE_LAMBDA);
-	
+
 		buf= new StringBuffer();
 		buf.append("package test;\n");
 		buf.append("\n");
@@ -519,12 +782,12 @@
 		buf.append("    }\n");
 		buf.append("}\n");
 		String expected1= buf.toString();
-	
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -564,10 +827,10 @@
 		buf.append("@FunctionalInterface interface ZV { void zoo(); }\n");
 		String original= buf.toString();
 		ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-		
+
 		enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
 		enable(CleanUpConstants.USE_LAMBDA);
-		
+
 		buf= new StringBuffer();
 		buf.append("package test;\n");
 		buf.append("public interface E {\n");
@@ -589,12 +852,12 @@
 		buf.append("@FunctionalInterface interface ZI { int  zoo(); }\n");
 		buf.append("@FunctionalInterface interface ZV { void zoo(); }\n");
 		String expected1= buf.toString();
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_LAMBDA);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -767,10 +1030,10 @@
 		buf.append("}\n");
 		String original= buf.toString();
 		ICompilationUnit cu1= pack1.createCompilationUnit("E.java", original, false, null);
-	
+
 		enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-	
+
 		buf= new StringBuffer();
 		buf.append("package test;\n");
 		buf.append("import java.util.*;\n");
@@ -803,12 +1066,12 @@
 		buf.append("    };\n");
 		buf.append("}\n");
 		String expected1= buf.toString();
-	
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-		
+
 		disable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
 		enable(CleanUpConstants.USE_LAMBDA);
-		
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
@@ -873,10 +1136,10 @@
 		buf.append("}\n");
 		String original= buf.toString();
 		ICompilationUnit cu1= pack1.createCompilationUnit("Test.java", original, false, null);
-	
+
 		enable(CleanUpConstants.CONVERT_FUNCTIONAL_INTERFACES);
 		enable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
-	
+
 		buf= new StringBuffer();
 		buf.append("package test;\n");
 		buf.append("\n");
@@ -892,12 +1155,12 @@
 		buf.append("    };\n");
 		buf.append("}\n");
 		String expected1= buf.toString();
-	
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 });
-	
+
 		disable(CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION);
 		enable(CleanUpConstants.USE_LAMBDA);
-	
+
 		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { original });
 	}
 
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java
index 67bb1bf..dbb6daa 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpConstantsOptions.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018, 2019 IBM Corporation and others.
+ * Copyright (c) 2018, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -68,6 +68,7 @@
 		options.setOption(CONVERT_FUNCTIONAL_INTERFACES, CleanUpOptions.FALSE);
 		options.setOption(USE_LAMBDA, CleanUpOptions.TRUE);
 		options.setOption(USE_ANONYMOUS_CLASS_CREATION, CleanUpOptions.FALSE);
+		options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpOptions.FALSE);
 
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
@@ -164,6 +165,7 @@
 		options.setOption(CONVERT_FUNCTIONAL_INTERFACES, CleanUpOptions.FALSE);
 		options.setOption(USE_LAMBDA, CleanUpOptions.TRUE);
 		options.setOption(USE_ANONYMOUS_CLASS_CREATION, CleanUpOptions.FALSE);
+		options.setOption(SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpOptions.FALSE);
 
 		//Unused Code
 		options.setOption(REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.FALSE);
diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml
index f3cb463..785fc83 100644
--- a/org.eclipse.jdt.ui/plugin.xml
+++ b/org.eclipse.jdt.ui/plugin.xml
@@ -7040,9 +7040,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.lambda">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.LambdaExpressionAndMethodRefCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref"
+            runAfter="org.eclipse.jdt.ui.cleanup.expressions">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.NumberSuffixCleanUp"
             id="org.eclipse.jdt.ui.cleanup.number_suffix"
-            runAfter="org.eclipse.jdt.ui.cleanup.expressions">
+            runAfter="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java
new file mode 100644
index 0000000..3fcdde4
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/LambdaExpressionAndMethodRefCleanUp.java
@@ -0,0 +1,475 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Fabrice TIERCELIN and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Fabrice TIERCELIN - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui.fix;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.CreationReference;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionMethodReference;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.LambdaExpression;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodReference;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeMethodReference;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix;
+import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+import org.eclipse.jdt.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+/**
+ * A fix that simplifies the lambda expression and the method reference syntax:
+ * <ul>
+ * <li>Parenthesis are not needed for a single untyped parameter,</li>
+ * <li>Return statement is not needed for a single expression,</li>
+ * <li>Brackets are not needed for a single statement,</li>
+ * <li>A lambda expression can be replaced by a creation or a method reference in some cases.</li>
+ * </ul>
+ */
+public class LambdaExpressionAndMethodRefCleanUp extends AbstractMultiFix {
+	public LambdaExpressionAndMethodRefCleanUp() {
+		this(Collections.emptyMap());
+	}
+
+	public LambdaExpressionAndMethodRefCleanUp(Map<String, String> options) {
+		super(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		boolean requireAST= isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF);
+		Map<String, String> requiredOptions= null;
+		return new CleanUpRequirements(requireAST, false, false, requiredOptions);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		if (isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF)) {
+			return new String[] { MultiFixMessages.LambdaExpressionAndMethodRefCleanUp_description };
+		}
+		return new String[0];
+	}
+
+	@SuppressWarnings("nls")
+	@Override
+	public String getPreview() {
+		StringBuilder bld= new StringBuilder();
+		bld.append("\n");
+
+		if (isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF)) {
+			bld.append("someString -> someString.trim().toLowerCase();\n");
+			bld.append("someString -> someString.trim().toLowerCase();\n");
+			bld.append("someString -> (someString.trim().toLowerCase() + \"bar\");\n");
+			bld.append("ArrayList::new;\n");
+			bld.append("Date::getTime;\n");
+		} else {
+			bld.append("(someString) -> someString.trim().toLowerCase();\n");
+			bld.append("someString -> {return someString.trim().toLowerCase();};\n");
+			bld.append("someString -> {return someString.trim().toLowerCase() + \"bar\";};\n");
+			bld.append("() -> new ArrayList<>();\n");
+			bld.append("date -> date.getTime();\n");
+		}
+
+		return bld.toString();
+	}
+
+	@Override
+	protected ICleanUpFix createFix(CompilationUnit unit) throws CoreException {
+		if (!isEnabled(CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF) || !JavaModelUtil.is18OrHigher(unit.getJavaElement().getJavaProject())) {
+			return null;
+		}
+
+		final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+		unit.accept(new ASTVisitor() {
+			@Override
+			public boolean visit(final LambdaExpression node) {
+				if (node.hasParentheses() && node.parameters().size() == 1
+						&& node.parameters().get(0) instanceof VariableDeclarationFragment) {
+					rewriteOperations.add(new RemoveParamParenthesesOperation(node));
+					return false;
+				} else if (node.getBody() instanceof Block) {
+					List<Statement> statements= ((Block) node.getBody()).statements();
+
+					if (statements.size() == 1 && statements.get(0) instanceof ReturnStatement) {
+						rewriteOperations.add(new RemoveReturnAndBracketsOperation(node, statements));
+						return false;
+					}
+				} else if (node.getBody() instanceof ClassInstanceCreation) {
+					ClassInstanceCreation ci= (ClassInstanceCreation) node.getBody();
+
+					List<Expression> arguments= ci.arguments();
+					if (node.parameters().size() == arguments.size() && areSameIdentifiers(node, arguments)) {
+						rewriteOperations.add(new ReplaceByCreationReferenceOperation(node, ci));
+						return false;
+					}
+				} else if (node.getBody() instanceof SuperMethodInvocation) {
+					SuperMethodInvocation smi= (SuperMethodInvocation) node.getBody();
+					List<Expression> arguments= smi.arguments();
+
+					if (node.parameters().size() == arguments.size() && areSameIdentifiers(node, arguments)) {
+						rewriteOperations.add(new ReplaceBySuperMethodReferenceOperation(node, smi));
+						return false;
+					}
+				} else if (node.getBody() instanceof MethodInvocation) {
+					MethodInvocation methodInvocation= (MethodInvocation) node.getBody();
+					Expression calledExpression= methodInvocation.getExpression();
+					List<Expression> arguments= methodInvocation.arguments();
+
+					if (node.parameters().size() == arguments.size()) {
+						if (!areSameIdentifiers(node, arguments)) {
+							return true;
+						}
+
+						IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
+
+						if (Boolean.TRUE.equals(ASTNodes.isStatic(methodInvocation))) {
+							if (methodBinding == null || methodBinding.getDeclaringClass() == null) {
+								return true;
+							}
+
+							ITypeBinding calledType= methodBinding.getDeclaringClass();
+
+							if (!arguments.isEmpty()) {
+								String[] remainingParams= new String[arguments.size() - 1];
+
+								for (int i= 0; i < arguments.size() - 1; i++) {
+									ITypeBinding resolveTypeBinding= arguments.get(i + 1).resolveTypeBinding();
+
+									if (resolveTypeBinding == null) {
+										return true;
+									}
+
+									remainingParams[i]= resolveTypeBinding.getQualifiedName();
+								}
+
+								for (IMethodBinding declaredMethodBinding : calledType.getDeclaredMethods()) {
+									if ((declaredMethodBinding.getModifiers() & Modifier.STATIC) == 0 && ASTNodes.usesGivenSignature(declaredMethodBinding,
+											calledType.getQualifiedName(), methodInvocation.getName().getIdentifier(), remainingParams)) {
+										return true;
+									}
+								}
+							}
+
+							rewriteOperations.add(new ReplaceByTypeReferenceOperation(node, methodInvocation, calledType));
+							return false;
+						}
+
+						if (calledExpression instanceof StringLiteral || calledExpression instanceof NumberLiteral
+								|| calledExpression instanceof ThisExpression) {
+							rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+							return false;
+						} else if (calledExpression instanceof FieldAccess) {
+							FieldAccess fieldAccess= (FieldAccess) calledExpression;
+
+							if (fieldAccess.resolveFieldBinding() != null && fieldAccess.resolveFieldBinding().isEffectivelyFinal()) {
+								rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+								return false;
+							}
+						} else if (calledExpression instanceof SuperFieldAccess) {
+							SuperFieldAccess fieldAccess= (SuperFieldAccess) calledExpression;
+
+							if (fieldAccess.resolveFieldBinding() != null && fieldAccess.resolveFieldBinding().isEffectivelyFinal()) {
+								rewriteOperations.add(new ReplaceByMethodReferenceOperation(node, methodInvocation));
+								return false;
+							}
+						}
+					} else if (calledExpression instanceof SimpleName && node.parameters().size() == arguments.size() + 1) {
+						SimpleName calledObject= (SimpleName) calledExpression;
+
+						if (isSameIdentifier(node, 0, calledObject)) {
+							for (int i= 0; i < arguments.size(); i++) {
+								ASTNode expression= ASTNodes.getUnparenthesedExpression(arguments.get(i));
+
+								if (!(expression instanceof SimpleName) || !isSameIdentifier(node, i + 1, (SimpleName) expression)) {
+									return true;
+								}
+							}
+
+							ITypeBinding clazz= null;
+							if (calledExpression.resolveTypeBinding() != null) {
+								clazz= calledExpression.resolveTypeBinding();
+							} else if (methodInvocation.resolveMethodBinding() != null && methodInvocation.resolveMethodBinding().getDeclaringClass() != null) {
+								clazz= methodInvocation.resolveMethodBinding().getDeclaringClass();
+							} else {
+								return true;
+							}
+
+							String[] cumulativeParams= new String[arguments.size() + 1];
+							cumulativeParams[0]= clazz.getQualifiedName();
+
+							for (int i= 0; i < arguments.size(); i++) {
+								ITypeBinding resolveTypeBinding= arguments.get(i).resolveTypeBinding();
+
+								if (resolveTypeBinding == null) {
+									return true;
+								}
+
+								cumulativeParams[i + 1]= resolveTypeBinding.getQualifiedName();
+							}
+
+							for (IMethodBinding declaredMethodBinding : clazz.getDeclaredMethods()) {
+								if ((declaredMethodBinding.getModifiers() & Modifier.STATIC) > 0 && ASTNodes.usesGivenSignature(declaredMethodBinding,
+										clazz.getQualifiedName(), methodInvocation.getName().getIdentifier(), cumulativeParams)) {
+									return true;
+								}
+							}
+
+							rewriteOperations.add(new ReplaceByTypeReferenceOperation(node, methodInvocation, clazz));
+							return false;
+						}
+					}
+				}
+
+				return true;
+			}
+
+			/**
+			 * In order to make parameters implicit, those ones should be the same in the same
+			 * order.
+			 *
+			 * @param node the lambda expression
+			 * @param arguments The arguments in the expression
+			 * @return true if the parameters are obvious
+			 */
+			private boolean areSameIdentifiers(LambdaExpression node, List<Expression> arguments) {
+				for (int i= 0; i < node.parameters().size(); i++) {
+					Expression expression= ASTNodes.getUnparenthesedExpression(arguments.get(i));
+
+					if (!(expression instanceof SimpleName) || !isSameIdentifier(node, i, (SimpleName) expression)) {
+						return false;
+					}
+				}
+
+				return true;
+			}
+
+			private boolean isSameIdentifier(final LambdaExpression node, final int i, final SimpleName argument) {
+				Object param0= node.parameters().get(i);
+
+				if (param0 instanceof VariableDeclarationFragment) {
+					VariableDeclarationFragment vdf= (VariableDeclarationFragment) param0;
+					return vdf.getName().getIdentifier().equals(argument.getIdentifier());
+				}
+
+				return false;
+			}
+		});
+
+		if (rewriteOperations.isEmpty()) {
+			return null;
+		}
+
+		return new CompilationUnitRewriteOperationsFix(MultiFixMessages.LambdaExpressionAndMethodRefCleanUp_description, unit,
+				rewriteOperations.toArray(new CompilationUnitRewriteOperation[rewriteOperations.size()]));
+	}
+
+	@Override
+	public boolean canFix(ICompilationUnit compilationUnit, IProblemLocation problem) {
+		return false;
+	}
+
+	@Override
+	protected ICleanUpFix createFix(CompilationUnit unit, IProblemLocation[] problems) throws CoreException {
+		return null;
+	}
+
+	private static class RemoveParamParenthesesOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+
+		public RemoveParamParenthesesOperation(final LambdaExpression node) {
+			this.node= node;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+
+			LambdaExpression copyOfLambdaExpression= ast.newLambdaExpression();
+			ASTNode copyOfParameter= rewrite.createCopyTarget((ASTNode) node.parameters().get(0));
+			copyOfLambdaExpression.parameters().add(copyOfParameter);
+			copyOfLambdaExpression.setBody(rewrite.createCopyTarget(node.getBody()));
+			copyOfLambdaExpression.setParentheses(false);
+			rewrite.replace(node, copyOfLambdaExpression, null);
+		}
+	}
+
+	private static class RemoveReturnAndBracketsOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+
+		private final List<Statement> statements;
+
+		public RemoveReturnAndBracketsOperation(final LambdaExpression node, final List<Statement> statements) {
+			this.node= node;
+			this.statements= statements;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+
+			ReturnStatement returnStatement= (ReturnStatement) statements.get(0);
+			Expression copyOfExpression= (Expression) rewrite.createCopyTarget(returnStatement.getExpression());
+			rewrite.replace(node.getBody(), ASTNodeFactory.parenthesizeIfNeeded(ast, copyOfExpression), null);
+		}
+	}
+
+	private static class ReplaceByCreationReferenceOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+
+		private final ClassInstanceCreation classInstanceCreation;
+
+		public ReplaceByCreationReferenceOperation(final LambdaExpression node, final ClassInstanceCreation classInstanceCreation) {
+			this.node= node;
+			this.classInstanceCreation= classInstanceCreation;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+
+			CreationReference creationRef= ast.newCreationReference();
+			creationRef.setType(copyType(cuRewrite, ast, classInstanceCreation, classInstanceCreation.resolveTypeBinding()));
+			rewrite.replace(node, creationRef, null);
+		}
+	}
+
+	private static class ReplaceBySuperMethodReferenceOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+
+		private final SuperMethodInvocation superMethodInvocation;
+
+		public ReplaceBySuperMethodReferenceOperation(final LambdaExpression node, final SuperMethodInvocation superMethodInvocation) {
+			this.node= node;
+			this.superMethodInvocation= superMethodInvocation;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+
+			SuperMethodReference creationRef= ast.newSuperMethodReference();
+			creationRef.setName((SimpleName) rewrite.createCopyTarget(superMethodInvocation.getName()));
+			rewrite.replace(node, creationRef, null);
+		}
+	}
+
+	private static class ReplaceByTypeReferenceOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+		private final MethodInvocation methodInvocation;
+
+		private final ITypeBinding type;
+
+		public ReplaceByTypeReferenceOperation(LambdaExpression node, MethodInvocation methodInvocation, ITypeBinding type) {
+			this.node= node;
+			this.methodInvocation= methodInvocation;
+			this.type= type;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+
+			TypeMethodReference typeMethodRef= ast.newTypeMethodReference();
+
+			typeMethodRef.setType(copyType(cuRewrite, ast, methodInvocation, type));
+			typeMethodRef.setName((SimpleName) rewrite.createCopyTarget(methodInvocation.getName()));
+			rewrite.replace(node, typeMethodRef, null);
+		}
+	}
+
+	private static class ReplaceByMethodReferenceOperation extends CompilationUnitRewriteOperation {
+		private final LambdaExpression node;
+
+		private final MethodInvocation methodInvocation;
+
+		public ReplaceByMethodReferenceOperation(LambdaExpression node, MethodInvocation methodInvocation) {
+			this.node= node;
+			this.methodInvocation= methodInvocation;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+			ExpressionMethodReference typeMethodRef= ast.newExpressionMethodReference();
+
+			if (methodInvocation.getExpression() != null) {
+				typeMethodRef.setExpression((Expression) rewrite.createCopyTarget(methodInvocation.getExpression()));
+			} else {
+				typeMethodRef.setExpression(ast.newThisExpression());
+			}
+
+			typeMethodRef.setName((SimpleName) rewrite.createCopyTarget(methodInvocation.getName()));
+			rewrite.replace(node, typeMethodRef, null);
+		}
+	}
+
+	private static Type copyType(final CompilationUnitRewrite cuRewrite, final AST ast, final ASTNode node, final ITypeBinding typeBinding) {
+		ImportRewrite importRewrite= cuRewrite.getImportRewrite();
+		ImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(node, importRewrite);
+		ITypeBinding modifiedType;
+
+		if (typeBinding.getTypeParameters().length == 0) {
+			modifiedType= typeBinding.getErasure();
+		} else {
+			modifiedType= typeBinding;
+		}
+
+		return ASTNodeFactory.newCreationType(ast, modifiedType, importRewrite, importContext);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java
index 7b6d156..e2bb676 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -76,6 +76,7 @@
 	public static String CodeStyleTabPage_CheckboxName_ConvertFunctionalInterfaces;
 	public static String CodeStyleTabPage_RadioName_UseLambdaWherePossible;
 	public static String CodeStyleTabPage_RadioName_UseAnonymous;
+	public static String CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax;
 
 	public static String ContributedCleanUpTabPage_ErrorPage_message;
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties
index 58c0a9b..f9e3770 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CleanUpMessages.properties
@@ -55,6 +55,7 @@
 CodeStyleTabPage_GroupName_FunctionalInterfaces=Functional interface instances
 CodeStyleTabPage_CheckboxName_ConvertFunctionalInterfaces=Con&vert functional interface instances
 CodeStyleTabPage_RadioName_UseLambdaWherePossible=Use lambda where possible
+CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax=Simplify &lambda expression and method reference syntax
 CodeStyleTabPage_RadioName_UseAnonymous=Use anonymous class
 ContributedCleanUpTabPage_ErrorPage_message=An error occurred while creating this page. See the error log for details
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
index f6ecb53..d49586b 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/CodeStyleTabPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@
 import org.eclipse.jdt.internal.ui.fix.ControlStatementsCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ConvertLoopCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ExpressionsCleanUp;
+import org.eclipse.jdt.internal.ui.fix.LambdaExpressionAndMethodRefCleanUp;
 import org.eclipse.jdt.internal.ui.fix.LambdaExpressionsCleanUp;
 import org.eclipse.jdt.internal.ui.fix.NumberSuffixCleanUp;
 import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp;
@@ -39,16 +40,17 @@
         		new ExpressionsCleanUp(values),
 				new NumberSuffixCleanUp(values),
 				new VariableDeclarationCleanUp(values),
-				new LambdaExpressionsCleanUp(values)
-        };
-    }
+				new LambdaExpressionsCleanUp(values),
+				new LambdaExpressionAndMethodRefCleanUp(values)
+		};
+	}
 
-    @Override
+	@Override
 	protected void doCreatePreferences(Composite composite, int numColumns) {
     	Group controlGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_ControlStatments);
 
-    	final CheckboxPreference useBlockPref= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseBlocks, CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, CleanUpModifyDialog.FALSE_TRUE);
-    	intent(controlGroup);
+		final CheckboxPreference useBlockPref= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseBlocks, CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, CleanUpModifyDialog.FALSE_TRUE);
+		intent(controlGroup);
 		final RadioPreference useBlockAlwaysPref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_AlwaysUseBlocks, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_ALWAYS, CleanUpModifyDialog.FALSE_TRUE);
 		intent(controlGroup);
 		final RadioPreference useBlockJDTStylePref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseBlocksSpecial, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NO_FOR_RETURN_AND_THROW, CleanUpModifyDialog.FALSE_TRUE);
@@ -56,12 +58,12 @@
 		final RadioPreference useBlockNeverPref= createRadioPref(controlGroup, numColumns - 1, CleanUpMessages.CodeStyleTabPage_RadioName_NeverUseBlocks, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NEVER, CleanUpModifyDialog.FALSE_TRUE);
 		registerSlavePreference(useBlockPref, new RadioPreference[] {useBlockAlwaysPref, useBlockJDTStylePref, useBlockNeverPref});
 
-    	CheckboxPreference convertLoop= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_ConvertForLoopToEnhanced, CleanUpConstants.CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED, CleanUpModifyDialog.FALSE_TRUE);
-    	registerPreference(convertLoop);
+		CheckboxPreference convertLoop= createCheckboxPref(controlGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_ConvertForLoopToEnhanced, CleanUpConstants.CONTROL_STATMENTS_CONVERT_FOR_LOOP_TO_ENHANCED, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(convertLoop);
 
-    	Group expressionsGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_Expressions);
+		Group expressionsGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_Expressions);
 
-    	final CheckboxPreference useParenthesesPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseParentheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpModifyDialog.FALSE_TRUE);
+		final CheckboxPreference useParenthesesPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseParentheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES, CleanUpModifyDialog.FALSE_TRUE);
 		intent(expressionsGroup);
 		final RadioPreference useParenthesesAlwaysPref= createRadioPref(expressionsGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_AlwaysUseParantheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpModifyDialog.FALSE_TRUE);
 		final RadioPreference useParenthesesNeverPref= createRadioPref(expressionsGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_NeverUseParantheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpModifyDialog.FALSE_TRUE);
@@ -75,7 +77,7 @@
 
 		Group variableGroup= createGroup(numColumns, composite, CleanUpMessages.CodeStyleTabPage_GroupName_VariableDeclarations);
 
-    	final CheckboxPreference useFinalPref= createCheckboxPref(variableGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinal, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, CleanUpModifyDialog.FALSE_TRUE);
+		final CheckboxPreference useFinalPref= createCheckboxPref(variableGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinal, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL, CleanUpModifyDialog.FALSE_TRUE);
 		intent(variableGroup);
 		final CheckboxPreference useFinalFieldsPref= createCheckboxPref(variableGroup, 1, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinalForFields, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PRIVATE_FIELDS, CleanUpModifyDialog.FALSE_TRUE);
 		final CheckboxPreference useFinalParametersPref= createCheckboxPref(variableGroup, 1, CleanUpMessages.CodeStyleTabPage_CheckboxName_UseFinalForParameters, CleanUpConstants.VARIABLE_DECLARATIONS_USE_FINAL_PARAMETERS, CleanUpModifyDialog.FALSE_TRUE);
@@ -89,5 +91,8 @@
 		RadioPreference useLambdaPref= createRadioPref(functionalInterfacesGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseLambdaWherePossible, CleanUpConstants.USE_LAMBDA, CleanUpModifyDialog.FALSE_TRUE);
 		RadioPreference useAnonymousPref= createRadioPref(functionalInterfacesGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_UseAnonymous, CleanUpConstants.USE_ANONYMOUS_CLASS_CREATION, CleanUpModifyDialog.FALSE_TRUE);
 		registerSlavePreference(convertFunctionalInterfaces, new RadioPreference[] { useLambdaPref, useAnonymousPref });
+
+		CheckboxPreference simplifyLambdaExpressionAndMethodRef= createCheckboxPref(functionalInterfacesGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_SimplifyLambdaExpressionAndMethodRefSyntax, CleanUpConstants.SIMPLIFY_LAMBDA_EXPRESSION_AND_METHOD_REF, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(simplifyLambdaExpressionAndMethodRef);
 	}
 }