Bug 571077: [16] Merge master branch to BETA_JAVA16 branch after 570855
for Y-builds

Change-Id: I7fc6b429c2065f92637e46eb60e3a321652d57ca
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 ab7973c..2ed3b55 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
@@ -132,6 +132,7 @@
 	public static String StaticInnerClassCleanUp_description;
 	public static String StringBuilderCleanUp_description;
 	public static String CodeStyleCleanUp_LazyLogical_description;
+	public static String PrimitiveComparisonCleanUp_description;
 	public static String PrimitiveParsingCleanUp_description;
 	public static String PrimitiveSerializationCleanUp_description;
 
@@ -140,6 +141,7 @@
 	public static String HashCleanup_description;
 
 	public static String RedundantModifiersCleanup_description;
+	public static String SubstringCleanUp_description;
 	public static String JoinCleanup_description;
 	public static String ArraysFillCleanUp_description;
 	public static String EvaluateNullableCleanUp_description;
@@ -150,6 +152,7 @@
 	public static String DoubleNegationCleanUp_description;
 	public static String RedundantComparisonStatementCleanup_description;
 	public static String RedundantSuperCallCleanup_description;
+	public static String UnreachableBlockCleanUp_description;
 	public static String TernaryOperatorCleanUp_description;
 	public static String StrictlyEqualOrDifferentCleanUp_description;
 	public static String MergeConditionalBlocksCleanup_description;
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 4ed57c3..8863cac 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
@@ -114,6 +114,7 @@
 StaticInnerClassCleanUp_description=Make inner classes static where possible
 StringBuilderCleanUp_description=Replace String concatenation by StringBuilder
 CodeStyleCleanUp_LazyLogical_description=Use lazy logical operator (&& and ||)
+PrimitiveComparisonCleanUp_description=Primitive comparison
 PrimitiveParsingCleanUp_description=Primitive parsing
 PrimitiveSerializationCleanUp_description=Primitive serialization
 
@@ -122,6 +123,7 @@
 HashCleanup_description=Use Objects.hash()
 
 RedundantModifiersCleanup_description=Remove redundant modifiers
+SubstringCleanUp_description=Redundant String.substring() parameter
 JoinCleanup_description=Use String.join()
 ArraysFillCleanUp_description=Use Arrays.fill() when possible
 EvaluateNullableCleanUp_description=Evaluate without null check
@@ -132,6 +134,7 @@
 DoubleNegationCleanUp_description=Double negation
 RedundantComparisonStatementCleanup_description=Remove redundant comparison statement
 RedundantSuperCallCleanup_description=Remove redundant super() call in constructor
+UnreachableBlockCleanUp_description=Unreachable block
 TernaryOperatorCleanUp_description=Replace (X && Y) || (!X && Z) by X ? Y : Z
 StrictlyEqualOrDifferentCleanUp_description=Use '==' or '^' on booleans
 MergeConditionalBlocksCleanup_description=Merge conditions of if/else if/else that have the same blocks
diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUpCore.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUpCore.java
new file mode 100644
index 0000000..619d0b3
--- /dev/null
+++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUpCore.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2021 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.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.manipulation.CleanUpContextCore;
+import org.eclipse.jdt.core.manipulation.CleanUpRequirementsCore;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
+import org.eclipse.jdt.internal.corext.fix.PrimitiveComparisonFixCore;
+
+public class PrimitiveComparisonCleanUpCore extends AbstractCleanUpCore {
+	public PrimitiveComparisonCleanUpCore(final Map<String, String> options) {
+		super(options);
+	}
+
+	public PrimitiveComparisonCleanUpCore() {
+	}
+
+	@Override
+	public CleanUpRequirementsCore getRequirementsCore() {
+		return new CleanUpRequirementsCore(requireAST(), false, false, null);
+	}
+
+	public boolean requireAST() {
+		return isEnabled(CleanUpConstants.PRIMITIVE_COMPARISON);
+	}
+
+	@Override
+	public ICleanUpFixCore createFixCore(final CleanUpContextCore context) throws CoreException {
+		CompilationUnit compilationUnit= context.getAST();
+
+		if (compilationUnit == null || !isEnabled(CleanUpConstants.PRIMITIVE_COMPARISON)) {
+			return null;
+		}
+
+		return PrimitiveComparisonFixCore.createCleanUp(compilationUnit);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		List<String> result= new ArrayList<>();
+
+		if (isEnabled(CleanUpConstants.PRIMITIVE_COMPARISON)) {
+			result.add(MultiFixMessages.PrimitiveComparisonCleanUp_description);
+		}
+
+		return result.toArray(new String[0]);
+	}
+
+	@Override
+	public String getPreview() {
+		if (isEnabled(CleanUpConstants.PRIMITIVE_COMPARISON)) {
+			return "int comparison = Integer.compare(number, anotherNumber);"; //$NON-NLS-1$
+		}
+
+		return "int comparison = Integer.valueOf(number).compareTo(anotherNumber);"; //$NON-NLS-1$
+	}
+}
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2Core.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2Core.java
index f731148..178019f 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2Core.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/codemanipulation/StubUtility2Core.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018, 2019 IBM Corporation and others.
+ * Copyright (c) 2018, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -761,7 +761,7 @@
 		IMethodBinding[] typeMethods= typeBinding.getDeclaredMethods();
 		for (IMethodBinding typeMethod : typeMethods) {
 			final int modifiers= typeMethod.getModifiers();
-			if (!typeMethod.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
+			if (!typeMethod.isConstructor() && !Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers) && !typeMethod.isSyntheticRecordMethod()) {
 				allMethods.add(typeMethod);
 			}
 		}
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 3ca898c..6e8b13c 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
@@ -762,6 +762,18 @@
 	public static final String USE_LAZY_LOGICAL_OPERATOR= "cleanup.lazy_logical_operator"; //$NON-NLS-1$
 
 	/**
+	 * Replaces the <code>compareTo()</code> method by a comparison on primitive.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.19
+	 */
+	public static final String PRIMITIVE_COMPARISON= "cleanup.primitive_comparison"; //$NON-NLS-1$
+
+	/**
 	 * Avoids to create primitive wrapper when parsing a string.
 	 * <p>
 	 * Possible values: {TRUE, FALSE}
@@ -1250,6 +1262,18 @@
 	public static final String REMOVE_REDUNDANT_MODIFIERS= "cleanup.remove_redundant_modifiers"; //$NON-NLS-1$
 
 	/**
+	 * Removes the second <code>substring()</code> parameter if this parameter is the length of the string.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.19
+	 */
+	public static final String SUBSTRING= "cleanup.substring"; //$NON-NLS-1$
+
+	/**
 	 * Replaces for loops to use String.join() where possible.
 	 * <p>
 	 * Possible values: {TRUE, FALSE}
@@ -1370,6 +1394,18 @@
 	public static final String REDUNDANT_SUPER_CALL= "cleanup.no_super"; //$NON-NLS-1$
 
 	/**
+	 * Detect two successive <code>if</code> conditions that are identical and remove the second one.
+	 * <p>
+	 * Possible values: {TRUE, FALSE}
+	 * <p>
+	 *
+	 * @see CleanUpOptionsCore#TRUE
+	 * @see CleanUpOptionsCore#FALSE
+	 * @since 4.19
+	 */
+	public static final String UNREACHABLE_BLOCK= "cleanup.unreachable_block"; //$NON-NLS-1$
+
+	/**
 	 * Replaces (X && Y) || (!X && Z) by X ? Y : Z.
 	 * <p>
 	 * Possible values: {TRUE, FALSE}
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java
index bcbb703..8c2070f 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.java
@@ -58,6 +58,7 @@
 	public static String ConvertIterableLoopOperation_semanticChangeWarning;
 	public static String ExpressionsFix_add_parentheses_change_name;
 	public static String ExpressionsFix_remove_parentheses_change_name;
+	public static String PrimitiveComparisonFix_convert_compareTo_to_primitive_comparison;
 	public static String ImportsFix_OrganizeImports_Description;
 	public static String Java50Fix_add_annotations_change_name;
 	public static String Java50Fix_add_type_parameters_change_name;
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties
index b463869..7cafc29 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/FixMessages.properties
@@ -105,6 +105,8 @@
 ConvertIterableLoopOperation_RemoveUpdateExpressions_Warning=The expressions in the update part of the loop will be removed.
 ImportsFix_OrganizeImports_Description=Organize Imports
 
+PrimitiveComparisonFix_convert_compareTo_to_primitive_comparison=Convert compareTo() method to a primitive comparison
+
 CleanUpPostSaveListener_name=Code Clean Up
 CleanUpPostSaveListener_SaveAction_ChangeName=Save Actions
 CleanUpPostSaveListener_SlowCleanUpDialog_link=If this happens again we recommend to disable the corresponding save actions on the <a>'Save Actions'</a> preference page.
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PrimitiveComparisonFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PrimitiveComparisonFixCore.java
new file mode 100644
index 0000000..638148f
--- /dev/null
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/PrimitiveComparisonFixCore.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2021 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.corext.fix;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+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.CastExpression;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+
+import org.eclipse.jdt.internal.ui.fix.MultiFixMessages;
+
+public class PrimitiveComparisonFixCore extends CompilationUnitRewriteOperationsFixCore {
+	public static final class PrimitiveComparisonFinder extends ASTVisitor {
+		private List<PrimitiveComparisonFixOperation> fResult;
+
+		public PrimitiveComparisonFinder(List<PrimitiveComparisonFixOperation> ops) {
+			fResult= ops;
+		}
+
+		@Override
+		public boolean visit(final MethodInvocation visited) {
+			if (visited.getExpression() != null
+					&& visited.arguments().size() == 1) {
+				Class<?>[] wrapperClasses= { Integer.class, Boolean.class, Long.class, Double.class, Character.class, Float.class, Short.class, Byte.class };
+
+				for (Class<?> wrapperClass : wrapperClasses) {
+					String canonicalName= wrapperClass.getCanonicalName();
+					String primitiveName= Bindings.getUnboxedTypeName(canonicalName);
+
+					if (ASTNodes.isPrimitive((Expression) visited.arguments().get(0), primitiveName)
+							&& ASTNodes.usesGivenSignature(visited, canonicalName, "compareTo", canonicalName)) { //$NON-NLS-1$
+						MethodInvocation methodInvocation = ASTNodes.as(visited.getExpression(), MethodInvocation.class);
+
+						if (methodInvocation != null
+								&& ASTNodes.usesGivenSignature(methodInvocation, canonicalName, "valueOf", primitiveName) //$NON-NLS-1$
+								&& ASTNodes.isPrimitive((Expression) methodInvocation.arguments().get(0), primitiveName)) {
+							fResult.add(new PrimitiveComparisonFixOperation(visited, (Expression) methodInvocation.arguments().get(0), wrapperClass));
+							return false;
+						}
+
+						ClassInstanceCreation classInstanceCreation = ASTNodes.as(visited.getExpression(), ClassInstanceCreation.class);
+
+						if (classInstanceCreation != null
+								&& ASTNodes.hasType(classInstanceCreation.getType().resolveBinding(), canonicalName)
+								&& ASTNodes.isPrimitive((Expression) classInstanceCreation.arguments().get(0), primitiveName)) {
+							fResult.add(new PrimitiveComparisonFixOperation(visited, (Expression) classInstanceCreation.arguments().get(0), wrapperClass));
+							return false;
+						}
+
+						CastExpression castExpression = ASTNodes.as(visited.getExpression(), CastExpression.class);
+
+						if (castExpression != null
+								&& ASTNodes.hasType(castExpression.getType().resolveBinding(), canonicalName)
+								&& ASTNodes.isPrimitive(castExpression.getExpression(), primitiveName)) {
+							fResult.add(new PrimitiveComparisonFixOperation(visited, castExpression.getExpression(), wrapperClass));
+							return false;
+						}
+
+						return true;
+					}
+				}
+			}
+
+			return true;
+		}
+	}
+
+	public static class PrimitiveComparisonFixOperation extends CompilationUnitRewriteOperation {
+		private final MethodInvocation visited;
+		private final Expression primitiveValue;
+		private final Class<?> wrapperClass;
+
+		public PrimitiveComparisonFixOperation(final MethodInvocation visited, final Expression primitiveValue, final Class<?> wrapperClass) {
+			this.visited= visited;
+			this.primitiveValue= primitiveValue;
+			this.wrapperClass= wrapperClass;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			AST ast= cuRewrite.getRoot().getAST();
+			TextEditGroup group= createTextEditGroup(MultiFixMessages.PrimitiveComparisonCleanUp_description, cuRewrite);
+			rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() {
+				@Override
+				public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
+					if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
+						return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
+					}
+
+					return super.computeSourceRange(nodeWithComment);
+				}
+			});
+
+
+			MethodInvocation compareMethod= ast.newMethodInvocation();
+			compareMethod.setExpression(ast.newSimpleName(wrapperClass.getSimpleName()));
+			compareMethod.setName(ast.newSimpleName("compare")); //$NON-NLS-1$
+			compareMethod.arguments().add(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression(primitiveValue)));
+			compareMethod.arguments().add(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression((Expression) visited.arguments().get(0))));
+
+			ASTNodes.replaceButKeepComment(rewrite, visited, compareMethod, group);
+		}
+	}
+
+
+	public static ICleanUpFixCore createCleanUp(final CompilationUnit compilationUnit) {
+		List<PrimitiveComparisonFixOperation> operations= new ArrayList<>();
+		PrimitiveComparisonFinder finder= new PrimitiveComparisonFinder(operations);
+		compilationUnit.accept(finder);
+
+		if (operations.isEmpty()) {
+			return null;
+		}
+
+		CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] ops= operations.toArray(new CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[0]);
+		return new PrimitiveComparisonFixCore(FixMessages.PrimitiveComparisonFix_convert_compareTo_to_primitive_comparison, compilationUnit, ops);
+	}
+
+	protected PrimitiveComparisonFixCore(final String name, final CompilationUnit compilationUnit, final CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation[] fixRewriteOperations) {
+		super(name, compilationUnit, fixRewriteOperations);
+	}
+}
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java
index 6c0e99b..5f8d49d 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 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
@@ -49,6 +49,7 @@
 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
 import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionMethodReference;
 import org.eclipse.jdt.core.dom.ExpressionStatement;
 import org.eclipse.jdt.core.dom.FieldAccess;
 import org.eclipse.jdt.core.dom.FieldDeclaration;
@@ -1070,6 +1071,9 @@
 		MessageSendFlowInfo info= createMessageSendFlowInfo();
 		setFlowInfo(node, info);
 		for (Expression arg : arguments) {
+			if (arg instanceof ExpressionMethodReference) {
+				arg= ((ExpressionMethodReference)arg).getExpression();
+			}
 			info.mergeArgument(getFlowInfo(arg), fFlowContext);
 		}
 		info.mergeReceiver(getFlowInfo(receiver), fFlowContext);
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPlugin.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPlugin.java
index 09feb5c..2350794 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPlugin.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPlugin.java
@@ -78,8 +78,6 @@
 
 	private BundleContext fBundleContext;
 
-	private static boolean fIsStopped= false;
-
 
 	public JUnitPlugin() {
 		fgPlugin= this;
@@ -250,7 +248,6 @@
 
 	@Override
 	public void stop(BundleContext context) throws Exception {
-		fIsStopped= true;
 		super.stop(context);
 		fBundleContext= null;
 	}
@@ -332,10 +329,6 @@
 		return null;
 	}
 
-	public static boolean isStopped() {
-		return fIsStopped;
-	}
-
 	public IDialogSettings getDialogSettingsSection(String name) {
 		IDialogSettings dialogSettings= getDialogSettings();
 		IDialogSettings section= dialogSettings.getSection(name);
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test327.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test327.java
new file mode 100644
index 0000000..e62aabb
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_in/A_test327.java
@@ -0,0 +1,13 @@
+package lambdaExpression18_in;
+
+import java.util.List;
+
+public class C1 {
+    void doIt() {
+        List<String> list= List.of("a", "b");
+        String search = "b";
+
+        /*[*/final boolean isFound = list.stream()
+                .anyMatch(search::equals);/*]*/
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test327.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test327.java
new file mode 100644
index 0000000..034c6af
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractMethodWorkSpace/ExtractMethodTests/lambdaExpression18_out/A_test327.java
@@ -0,0 +1,17 @@
+package lambdaExpression18_in;
+
+import java.util.List;
+
+public class C1 {
+    void doIt() {
+        List<String> list= List.of("a", "b");
+        String search = "b";
+
+        extracted(list, search);
+    }
+
+	private void extracted(List<String> list, String search) {
+		/*[*/final boolean isFound = list.stream()
+                .anyMatch(search::equals);/*]*/
+	}
+}
\ No newline at end of file
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 e2bdf96..88702f9 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
@@ -321,4 +321,10 @@
 	public void test326() throws Exception {
 		lambdaExpressionTest(0, Modifier.PUBLIC);
 	}
+
+	@Test
+	public void test327() throws Exception {
+		lambdaExpressionTest(0, Modifier.PRIVATE);
+	}
+
 }
diff --git a/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java b/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java
index d5da16f..3457153 100644
--- a/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java
+++ b/org.eclipse.jdt.ui.tests/performance/org/eclipse/jdt/ui/tests/performance/views/CleanUpPerfTest.java
@@ -82,6 +82,7 @@
 import org.eclipse.jdt.internal.ui.fix.LazyLogicalCleanUp;
 import org.eclipse.jdt.internal.ui.fix.MapCloningCleanUp;
 import org.eclipse.jdt.internal.ui.fix.MergeConditionalBlocksCleanUp;
+import org.eclipse.jdt.internal.ui.fix.PrimitiveComparisonCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ReduceIndentationCleanUp;
 import org.eclipse.jdt.internal.ui.fix.SingleUsedFieldCleanUp;
 import org.eclipse.jdt.internal.ui.fix.SortMembersCleanUp;
@@ -749,6 +750,22 @@
 	}
 
 	@Test
+	public void testPrimitiveComparisonCleanUp() throws Exception {
+		CleanUpRefactoring cleanUpRefactoring= new CleanUpRefactoring();
+		addAllCUs(cleanUpRefactoring, MyTestSetup.fJProject1.getChildren());
+
+		Map<String, String> node= getNullSettings();
+
+		node.put(CleanUpConstants.PRIMITIVE_COMPARISON, CleanUpOptions.TRUE);
+
+		storeSettings(node);
+
+		cleanUpRefactoring.addCleanUp(new PrimitiveComparisonCleanUp());
+
+		doCleanUp(cleanUpRefactoring);
+	}
+
+	@Test
 	public void testMapCloningCleanUp() throws Exception {
 		CleanUpRefactoring cleanUpRefactoring= new CleanUpRefactoring();
 		addAllCUs(cleanUpRefactoring, MyTestSetup.fJProject1.getChildren());
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java
index 4393330..65d5516 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest.java
@@ -4131,6 +4131,93 @@
 	}
 
 	@Test
+	public void testSubstring() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String given= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    private String textInInstance = \"foo\";\n" //
+				+ "\n" //
+				+ "    public String reduceSubstring(String text) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return text.substring(2, text.length());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String reduceSubstringOnField() {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return textInInstance.substring(3, textInInstance.length());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String reduceSubstringOnExpression(String text) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return (textInInstance + text).substring(4, (textInInstance + text).length());\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null);
+
+		enable(CleanUpConstants.SUBSTRING);
+
+		String expected= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    private String textInInstance = \"foo\";\n" //
+				+ "\n" //
+				+ "    public String reduceSubstring(String text) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return text.substring(2);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String reduceSubstringOnField() {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return textInInstance.substring(3);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String reduceSubstringOnExpression(String text) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return (textInInstance + text).substring(4);\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		assertNotEquals("The class must be changed", given, expected);
+		assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.SubstringCleanUp_description)));
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected });
+	}
+
+	@Test
+	public void testKeepSubstring() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import java.util.List;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public String doNotReduceSubstringOnOtherExpression(String text) {\n" //
+				+ "        return text.substring(5, text.hashCode());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotReduceSubstringOnConstant(String text) {\n" //
+				+ "        return text.substring(6, 123);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotReduceSubstringOnDifferentVariable(String text1, String text2) {\n" //
+				+ "        return text1.substring(7, text2.length());\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotReduceSubstringOnActiveExpression(List<String> texts) {\n" //
+				+ "        return texts.remove(0).substring(7, texts.remove(0).length());\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.SUBSTRING);
+
+		assertRefactoringHasNoChange(new ICompilationUnit[] { cu });
+	}
+
+	@Test
 	public void testUseArraysFill() throws Exception {
 		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
 		String input= "" //
@@ -4669,6 +4756,160 @@
 	}
 
 	@Test
+	public void testPrimitiveComparison() throws Exception {
+		// Given
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String given= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public int simplifyIntegerComparison(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyDoubleComparison(double number, double anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Double.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyFloatComparison(float number, float anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Float.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyShortComparison(short number, short anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Short.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyLongComparison(long number, long anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Long.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyCharacterComparison(char number, char anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Character.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyByteComparison(byte number, byte anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Byte.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyBooleanComparison(boolean number, boolean anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Boolean.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int refactorIntegerInstantiation(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return new Integer(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int refactorIntegerCast(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return ((Integer) number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		String expected= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public int simplifyIntegerComparison(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyDoubleComparison(double number, double anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Double.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyFloatComparison(float number, float anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Float.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyShortComparison(short number, short anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Short.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyLongComparison(long number, long anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Long.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyCharacterComparison(char number, char anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Character.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyByteComparison(byte number, byte anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Byte.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int simplifyBooleanComparison(boolean number, boolean anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Boolean.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int refactorIntegerInstantiation(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int refactorIntegerCast(int number, int anotherNumber) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        return Integer.compare(number, anotherNumber);\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		// When
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null);
+		enable(CleanUpConstants.PRIMITIVE_COMPARISON);
+
+		// Then
+		assertNotEquals("The class must be changed", given, expected);
+		assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.PrimitiveComparisonCleanUp_description)));
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected });
+	}
+
+	@Test
+	public void testDoNotUsePrimitiveComparison() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public int doNotRefactorWrapper(Integer number, int anotherNumber) {\n" //
+				+ "        return Integer.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRefactorWrapperComparator(int number, Integer anotherNumber) {\n" //
+				+ "        return Integer.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRefactorString(String number, int anotherNumber) {\n" //
+				+ "        return Integer.valueOf(number).compareTo(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRefactorBadMethod(int number, int anotherNumber) {\n" //
+				+ "        return Integer.valueOf(number).valueOf(anotherNumber);\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.PRIMITIVE_COMPARISON);
+
+		assertRefactoringHasNoChange(new ICompilationUnit[] { cu });
+	}
+
+	@Test
 	public void testPrimitiveParsing() throws Exception {
 		// Given
 		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
@@ -6877,6 +7118,315 @@
 	}
 
 	@Test
+	public void testUnreachableBlock() throws Exception {
+		// Given
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String given= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import java.io.IOException;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public int removeDuplicateCondition(boolean isValid, boolean isFound) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (isValid && isFound) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isFound && isValid) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithElse(int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            return 1;\n" //
+				+ "        } else {\n" //
+				+ "            return 2;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithSeveralConditions(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            return 1;\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            return 2;\n" //
+				+ "        } else {\n" //
+				+ "            return 3;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithFollowingCode(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            return 1;\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            return 2;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 3;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithoutFallingThrough(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            System.out.println(\"I do not fall through\");\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            System.out.println(\"I do not fall through too\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 3;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionAmongOthers(int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 == 0) {\n" //
+				+ "            return -1;\n" //
+				+ "        } else if (i1 < i2 + 1) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (1 + i2 > i1) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public void removeDuplicateConditionWithoutUnreachableCode(boolean isActive, boolean isFound) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (isActive && isFound) {\n" //
+				+ "            System.out.println(\"I fall through\");\n" //
+				+ "            return;\n" //
+				+ "        } else if (isFound && isActive) {\n" //
+				+ "            System.out.println(\"I do not fall through\");\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeUncaughtCode(boolean b1, boolean b2) throws IOException {\n" //
+				+ "        try {\n" //
+				+ "            // Keep this comment\n" //
+				+ "            if (b1 && b2) {\n" //
+				+ "                return 0;\n" //
+				+ "            } else if (b2 && b1) {\n" //
+				+ "                throw new IOException();\n" //
+				+ "            }\n" //
+				+ "        } catch (NullPointerException e) {\n" //
+				+ "            System.out.println(\"I should be reachable\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		String expected= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import java.io.IOException;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public int removeDuplicateCondition(boolean isValid, boolean isFound) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (isValid && isFound) {\n" //
+				+ "            return 0;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithElse(int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else {\n" //
+				+ "            return 2;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithSeveralConditions(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            return 1;\n" //
+				+ "        } else {\n" //
+				+ "            return 3;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithFollowingCode(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 3;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionWithoutFallingThrough(boolean isActive, int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isActive) {\n" //
+				+ "            System.out.println(\"I do not fall through\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 3;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeDuplicateConditionAmongOthers(int i1, int i2) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (i1 == 0) {\n" //
+				+ "            return -1;\n" //
+				+ "        } else if (i1 < i2 + 1) {\n" //
+				+ "            return 0;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public void removeDuplicateConditionWithoutUnreachableCode(boolean isActive, boolean isFound) {\n" //
+				+ "        // Keep this comment\n" //
+				+ "        if (isActive && isFound) {\n" //
+				+ "            System.out.println(\"I fall through\");\n" //
+				+ "            return;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int removeUncaughtCode(boolean b1, boolean b2) throws IOException {\n" //
+				+ "        try {\n" //
+				+ "            // Keep this comment\n" //
+				+ "            if (b1 && b2) {\n" //
+				+ "                return 0;\n" //
+				+ "            }\n" //
+				+ "        } catch (NullPointerException e) {\n" //
+				+ "            System.out.println(\"I should be reachable\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "}\n";
+
+		// When
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", given, false, null);
+		enable(CleanUpConstants.UNREACHABLE_BLOCK);
+
+		// Then
+		assertNotEquals("The class must be changed", given, expected);
+		assertGroupCategoryUsed(new ICompilationUnit[] { cu }, new HashSet<>(Arrays.asList(MultiFixMessages.UnreachableBlockCleanUp_description)));
+		assertRefactoringResultAsExpected(new ICompilationUnit[] { cu }, new String[] { expected });
+	}
+
+	@Test
+	public void testKeepUnreachableBlock() throws Exception {
+		IPackageFragment pack= fSourceFolder.createPackageFragment("test1", false, null);
+		String sample= "" //
+				+ "package test1;\n" //
+				+ "\n" //
+				+ "import java.io.IOException;\n" //
+				+ "import java.util.List;\n" //
+				+ "\n" //
+				+ "public class E {\n" //
+				+ "    public String doNotCreateUnreachable(int i1, int i2) {\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return \"Falls through\";\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            System.out.println(\"Does not fall through\");\n" //
+				+ "        } else {\n" //
+				+ "            return \"Falls through too\";\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return \"I should be reachable\";\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotCreateUnreachableOverSeveralConditions(boolean isEnabled, int i1, int i2) {\n" //
+				+ "        if (i1 < i2) {\n" //
+				+ "            return \"Falls through\";\n" //
+				+ "        } else if (isEnabled) {\n" //
+				+ "            return \"Falls through too\";\n" //
+				+ "        } else if (i2 > i1) {\n" //
+				+ "            System.out.println(\"Does not fall through\");\n" //
+				+ "        } else {\n" //
+				+ "            return \"Falls through also\";\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return \"I should be reachable\";\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRemoveDifferentCondition(boolean b1, boolean b2) {\n" //
+				+ "        if (b1 && b2) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (b2 || b1) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRemoveActiveCondition(List<String> myList) {\n" //
+				+ "        if (myList.remove(\"I will be removed\")) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (myList.remove(\"I will be removed\")) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public String doNotRemoveConditionPrecededByActiveCondition(int number) {\n" //
+				+ "        if (number > 0) {\n" //
+				+ "            return \"Falls through\";\n" //
+				+ "        } else if (number++ == 42) {\n" //
+				+ "            return \"Active condition\";\n" //
+				+ "        } else if (number > 0) {\n" //
+				+ "            return \"Falls through too\";\n" //
+				+ "        } else {\n" //
+				+ "            return \"Falls through also\";\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRemoveCaughtCode(boolean b1, boolean b2) {\n" //
+				+ "        try {\n" //
+				+ "            if (b1 && b2) {\n" //
+				+ "                return 0;\n" //
+				+ "            } else if (b2 && b1) {\n" //
+				+ "                throw new IOException();\n" //
+				+ "            }\n" //
+				+ "        } catch (IOException e) {\n" //
+				+ "            System.out.println(\"I should be reachable\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "\n" //
+				+ "    public int doNotRemoveThrowingExceptionCode(boolean isValid, int number) {\n" //
+				+ "        if (isValid || true) {\n" //
+				+ "            return 0;\n" //
+				+ "        } else if (isValid || true || ((number / 0) == 42)) {\n" //
+				+ "            return 1;\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        return 2;\n" //
+				+ "    }\n" //
+				+ "}\n";
+		ICompilationUnit cu= pack.createCompilationUnit("E.java", sample, false, null);
+
+		enable(CleanUpConstants.UNREACHABLE_BLOCK);
+
+		assertRefactoringHasNoChange(new ICompilationUnit[] { cu });
+	}
+
+	@Test
 	public void testMergeConditionalBlocks() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
 		String sample= "" //
@@ -13918,6 +14468,8 @@
 		String sample= "" //
 				+ "package test1;\n" //
 				+ "\n" //
+				+ "import java.sql.DriverPropertyInfo;\n" //
+				+ "\n" //
 				+ "public class E {\n" //
 				+ "    public interface DoNotRefactorInnerInterface {\n" //
 				+ "        boolean anotherMethod();\n" //
@@ -13931,6 +14483,18 @@
 				+ "        }\n" //
 				+ "    }\n" //
 				+ "\n" //
+				+ "    public class DoNotRefactorClassUsingInheritedMemberAsItSNotHandledYet extends DriverPropertyInfo {\n" //
+				+ "        private static final long serialVersionUID = 1L;\n" //
+				+ "\n" //
+				+ "        public DoNotRefactorClassUsingInheritedMemberAsItSNotHandledYet() {\n" //
+				+ "            super(\"\", \"\");\n" //
+				+ "        }\n" //
+				+ "\n" //
+				+ "        public boolean itSNotHandledYet() {\n" //
+				+ "            return choices != null;\n" //
+				+ "        }\n" //
+				+ "    }\n" //
+				+ "\n" //
 				+ "    public class DoNotRefactorInnerClassThatUsesMethod {\n" //
 				+ "        int i;\n" //
 				+ "\n" //
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/forceQualifierUpdate.txt b/org.eclipse.jdt.ui.unittest.junit.feature/forceQualifierUpdate.txt
new file mode 100644
index 0000000..51c07ff
--- /dev/null
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/forceQualifierUpdate.txt
@@ -0,0 +1,2 @@
+# To force a version qualifier update add the bug here
+Bug 570855 - Include org.eclipse.jdt.ui.unittest.junit.feature in p2 site 
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml b/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml
index 4f9eed7..f9bb14b 100644
--- a/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml
+++ b/org.eclipse.jdt.ui.unittest.junit.feature/pom.xml
@@ -22,4 +22,36 @@
   <artifactId>org.eclipse.jdt.ui.unittest.junit.feature</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <packaging>eclipse-feature</packaging>
+  <build>
+     <plugins>
+      <plugin>
+        <groupId>org.eclipse.tycho</groupId>
+        <artifactId>tycho-source-plugin</artifactId>
+        <version>${tycho.version}</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <id>feature-source</id>
+            <goals>
+              <goal>feature-source</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.eclipse.tycho</groupId>
+        <artifactId>tycho-p2-plugin</artifactId>
+        <version>${tycho.version}</version>
+        <executions>
+          <execution>
+            <id>attach-p2-metadata</id>
+            <phase>package</phase>
+            <goals>
+              <goal>p2-metadata</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
 </project>
diff --git a/org.eclipse.jdt.ui/.settings/.api_filters b/org.eclipse.jdt.ui/.settings/.api_filters
index 0a4cd5e..40dac27 100644
--- a/org.eclipse.jdt.ui/.settings/.api_filters
+++ b/org.eclipse.jdt.ui/.settings/.api_filters
@@ -24,6 +24,13 @@
                 <message_argument value="isRecordComponent()"/>
             </message_arguments>
         </filter>
+        <filter comment="Java 15 preview feature" id="640712815">
+            <message_arguments>
+                <message_argument value="IMethodBinding"/>
+                <message_argument value="RefactoringAvailabilityTester"/>
+                <message_argument value="isCanonicalConstructor()"/>
+            </message_arguments>
+        </filter>
     </resource>
     <resource path="core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/IntroduceFactoryRefactoring.java" type="org.eclipse.jdt.internal.corext.refactoring.code.IntroduceFactoryRefactoring">
         <filter comment="This is a Java 15 preview feature" id="640712815">
@@ -364,6 +371,15 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java" type="org.eclipse.jdt.ui.actions.GenerateNewConstructorUsingFieldsAction">
+        <filter comment="This is a Java15 preview feature" id="640712815">
+            <message_arguments>
+                <message_argument value="IType"/>
+                <message_argument value="GenerateNewConstructorUsingFieldsAction"/>
+                <message_argument value="isRecord()"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="ui/org/eclipse/jdt/ui/actions/IJavaEditorActionDefinitionIds.java" type="org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds">
         <filter id="571473929">
             <message_arguments>
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 38abe1a..41c5716 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
@@ -53,7 +53,6 @@
 		options.setOption(EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.FALSE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpOptions.TRUE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpOptions.FALSE);
-		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
 		options.setOption(EXTRACT_INCREMENT, CleanUpOptions.FALSE);
 		options.setOption(PULL_UP_ASSIGNMENT, CleanUpOptions.FALSE);
 
@@ -80,6 +79,7 @@
 		options.setOption(STATIC_INNER_CLASS, CleanUpOptions.FALSE);
 		options.setOption(STRINGBUILDER, CleanUpOptions.FALSE);
 		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
+		options.setOption(PRIMITIVE_COMPARISON, CleanUpOptions.FALSE);
 		options.setOption(PRIMITIVE_PARSING, CleanUpOptions.FALSE);
 		options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE);
 
@@ -96,12 +96,14 @@
 		options.setOption(REMOVE_UNNECESSARY_CASTS, CleanUpOptions.TRUE);
 		options.setOption(REMOVE_UNNECESSARY_NLS_TAGS, CleanUpOptions.TRUE);
 		options.setOption(INSERT_INFERRED_TYPE_ARGUMENTS, CleanUpOptions.FALSE);
+		options.setOption(SUBSTRING, CleanUpOptions.FALSE);
 		options.setOption(ARRAYS_FILL, CleanUpOptions.FALSE);
 		options.setOption(EVALUATE_NULLABLE, CleanUpOptions.FALSE);
 		options.setOption(PUSH_DOWN_NEGATION, CleanUpOptions.FALSE);
 		options.setOption(DOUBLE_NEGATION, CleanUpOptions.FALSE);
 		options.setOption(REMOVE_REDUNDANT_COMPARISON_STATEMENT, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_SUPER_CALL, CleanUpOptions.FALSE);
+		options.setOption(UNREACHABLE_BLOCK, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_FALLING_THROUGH_BLOCK_END, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_IF_CONDITION, CleanUpOptions.FALSE);
 		options.setOption(USE_DIRECTLY_MAP_METHOD, CleanUpOptions.FALSE);
@@ -148,15 +150,16 @@
 		options.setOption(MODERNIZE_HASH, CleanUpOptions.FALSE);
 		options.setOption(USE_OBJECTS_EQUALS, CleanUpOptions.FALSE);
 
+		// Code fixing
+		options.setOption(STANDARD_COMPARISON, CleanUpOptions.FALSE);
+		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
+
 		// Duplicate Code
 		options.setOption(TERNARY_OPERATOR, CleanUpOptions.FALSE);
 		options.setOption(STRICTLY_EQUAL_OR_DIFFERENT, CleanUpOptions.FALSE);
 		options.setOption(MERGE_CONDITIONAL_BLOCKS, CleanUpOptions.FALSE);
 		options.setOption(CONTROLFLOW_MERGE, CleanUpOptions.FALSE);
 
-		// Code fixing
-		options.setOption(STANDARD_COMPARISON, CleanUpOptions.FALSE);
-
 		// Java Features
 		options.setOption(USE_PATTERN_MATCHING_FOR_INSTANCEOF, CleanUpOptions.FALSE);
 		options.setOption(CONTROL_STATEMENTS_CONVERT_TO_SWITCH_EXPRESSIONS, CleanUpOptions.FALSE);
@@ -205,7 +208,6 @@
 		options.setOption(EXPRESSIONS_USE_PARENTHESES, CleanUpOptions.FALSE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpOptions.TRUE);
 		options.setOption(EXPRESSIONS_USE_PARENTHESES_ALWAYS, CleanUpOptions.FALSE);
-		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
 		options.setOption(EXTRACT_INCREMENT, CleanUpOptions.FALSE);
 		options.setOption(PULL_UP_ASSIGNMENT, CleanUpOptions.FALSE);
 
@@ -232,6 +234,7 @@
 		options.setOption(STATIC_INNER_CLASS, CleanUpOptions.FALSE);
 		options.setOption(STRINGBUILDER, CleanUpOptions.FALSE);
 		options.setOption(USE_LAZY_LOGICAL_OPERATOR, CleanUpOptions.FALSE);
+		options.setOption(PRIMITIVE_COMPARISON, CleanUpOptions.FALSE);
 		options.setOption(PRIMITIVE_PARSING, CleanUpOptions.FALSE);
 		options.setOption(PRIMITIVE_SERIALIZATION, CleanUpOptions.FALSE);
 
@@ -248,12 +251,14 @@
 		options.setOption(REMOVE_UNNECESSARY_CASTS, CleanUpOptions.TRUE);
 		options.setOption(REMOVE_UNNECESSARY_NLS_TAGS, CleanUpOptions.FALSE);
 		options.setOption(INSERT_INFERRED_TYPE_ARGUMENTS, CleanUpOptions.FALSE);
+		options.setOption(SUBSTRING, CleanUpOptions.FALSE);
 		options.setOption(ARRAYS_FILL, CleanUpOptions.FALSE);
 		options.setOption(EVALUATE_NULLABLE, CleanUpOptions.FALSE);
 		options.setOption(PUSH_DOWN_NEGATION, CleanUpOptions.FALSE);
 		options.setOption(DOUBLE_NEGATION, CleanUpOptions.FALSE);
 		options.setOption(REMOVE_REDUNDANT_COMPARISON_STATEMENT, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_SUPER_CALL, CleanUpOptions.FALSE);
+		options.setOption(UNREACHABLE_BLOCK, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_FALLING_THROUGH_BLOCK_END, CleanUpOptions.FALSE);
 		options.setOption(REDUNDANT_IF_CONDITION, CleanUpOptions.FALSE);
 		options.setOption(USE_DIRECTLY_MAP_METHOD, CleanUpOptions.FALSE);
@@ -268,9 +273,6 @@
 		options.setOption(REMOVE_USELESS_CONTINUE, CleanUpOptions.FALSE);
 		options.setOption(UNLOOPED_WHILE, CleanUpOptions.FALSE);
 
-		// Code fixing
-		options.setOption(STANDARD_COMPARISON, CleanUpOptions.FALSE);
-
 		//Missing Code
 		options.setOption(ADD_MISSING_ANNOTATIONS, CleanUpOptions.TRUE);
 		options.setOption(ADD_MISSING_ANNOTATIONS_OVERRIDE, CleanUpOptions.TRUE);
@@ -305,6 +307,10 @@
 
 		options.setOption(CLEANUP_ON_SAVE_ADDITIONAL_OPTIONS, CleanUpOptions.FALSE);
 
+		// Code fixing
+		options.setOption(STANDARD_COMPARISON, CleanUpOptions.FALSE);
+		options.setOption(CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpOptions.FALSE);
+
 		// Duplicate Code
 		options.setOption(TERNARY_OPERATOR, CleanUpOptions.FALSE);
 		options.setOption(STRICTLY_EQUAL_OR_DIFFERENT, CleanUpOptions.FALSE);
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRegistry.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRegistry.java
index dcd7d3e..66829f9 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRegistry.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/CleanUpRegistry.java
@@ -434,7 +434,7 @@
 		Arrays.sort(fPageDescriptors, (o1, o2) -> {
 			String name1= o1.getName();
 			String name2= o2.getName();
-			return Collator.getInstance().compare(name1.replaceAll("&", ""), name2.replaceAll("&", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			return Collator.getInstance().compare(name1.replace("&", ""), name2.replace("&", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 		});
 	}
 
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java
index 57669bc..46a38a3 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ChangeSignatureProcessor.java
@@ -1275,7 +1275,7 @@
 				if (info.isDeleted())
 					buffer.append("{deleted}"); //$NON-NLS-1$
 				else
-					buffer.append(info.getNewTypeName().replaceAll(" ", ""));  //$NON-NLS-1$//$NON-NLS-2$
+					buffer.append(info.getNewTypeName().replace(" ", ""));  //$NON-NLS-1$//$NON-NLS-2$
 				buffer.append(" "); //$NON-NLS-1$
 				if (info.isDeleted())
 					buffer.append("{deleted}"); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.ui/plugin.properties b/org.eclipse.jdt.ui/plugin.properties
index e33b7c2..b684b6a 100644
--- a/org.eclipse.jdt.ui/plugin.properties
+++ b/org.eclipse.jdt.ui/plugin.properties
@@ -1214,6 +1214,7 @@
 #--- Clean Up ---
 CleanUpTabPage.CodeStyle.name = &Code Style
 CleanUpTabPage.JavaFeature.name = &Java Feature
+CleanUpTabPage.SourceFixing.name = Source &Fixing
 CleanUpTabPage.Optimization.name = O&ptimization
 CleanUpTabPage.MemberAccesses.name = Membe&r Accesses
 CleanUpTabPage.UnnecessaryCode.name = &Unnecessary Code
diff --git a/org.eclipse.jdt.ui/plugin.xml b/org.eclipse.jdt.ui/plugin.xml
index e1a26c1..cc1e418 100644
--- a/org.eclipse.jdt.ui/plugin.xml
+++ b/org.eclipse.jdt.ui/plugin.xml
@@ -7009,6 +7009,11 @@
             cleanUpKind="cleanUp">
       </cleanUpConfigurationUI>
       <cleanUpConfigurationUI
+            class="org.eclipse.jdt.internal.ui.preferences.cleanup.SourceFixingTabPage"
+            name="%CleanUpTabPage.SourceFixing.name"
+            cleanUpKind="cleanUp">
+      </cleanUpConfigurationUI>
+      <cleanUpConfigurationUI
             class="org.eclipse.jdt.internal.ui.preferences.cleanup.OptimizationTabPage"
             name="%CleanUpTabPage.Optimization.name"
             cleanUpKind="saveAction">
@@ -7098,14 +7103,9 @@
             runAfter="org.eclipse.jdt.ui.cleanup.expressions">
       </cleanUp>
       <cleanUp
-            class="org.eclipse.jdt.internal.ui.fix.BitwiseConditionalExpressionCleanup"
-            id="org.eclipse.jdt.ui.cleanup.bitwise_conditional_expression"
-            runAfter="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref">
-      </cleanUp>
-      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.ExtractIncrementCleanUp"
             id="org.eclipse.jdt.ui.cleanup.extract_increment"
-            runAfter="org.eclipse.jdt.ui.cleanup.bitwise_conditional_expression">
+            runAfter="org.eclipse.jdt.ui.cleanup.lambda_and_method_ref">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.PullUpAssignmentCleanUp"
@@ -7143,9 +7143,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.stringbuilder">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.PrimitiveComparisonCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.primitive_comparison"
+            runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.PrimitiveParsingCleanUp"
             id="org.eclipse.jdt.ui.cleanup.primitive_parsing"
-            runAfter="org.eclipse.jdt.ui.cleanup.lazy_logical">
+            runAfter="org.eclipse.jdt.ui.cleanup.primitive_comparison">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.PrimitiveSerializationCleanUp"
@@ -7248,9 +7253,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.imports">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.SubstringCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.substring"
+            runAfter="org.eclipse.jdt.ui.cleanup.format">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.ArraysFillCleanUp"
             id="org.eclipse.jdt.ui.cleanup.arrays_fill"
-            runAfter="org.eclipse.jdt.ui.cleanup.format">
+            runAfter="org.eclipse.jdt.ui.cleanup.substring">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.EvaluateNullableCleanUp"
@@ -7283,9 +7293,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.comparison_statement">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.UnreachableBlockCleanUp"
+            id="org.eclipse.jdt.ui.cleanup.unreachable_block"
+            runAfter="org.eclipse.jdt.ui.cleanup.no_super">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.MapMethodCleanUp"
             id="org.eclipse.jdt.ui.cleanup.use_directly_map_method"
-            runAfter="org.eclipse.jdt.ui.cleanup.no_super">
+            runAfter="org.eclipse.jdt.ui.cleanup.unreachable_block">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.CollectionCloningCleanUp"
@@ -7303,9 +7318,14 @@
             runAfter="org.eclipse.jdt.ui.cleanup.map_cloning">
       </cleanUp>
       <cleanUp
+            class="org.eclipse.jdt.internal.ui.fix.BitwiseConditionalExpressionCleanup"
+            id="org.eclipse.jdt.ui.cleanup.bitwise_conditional_expression"
+            runAfter="org.eclipse.jdt.ui.cleanup.standard_comparison">
+      </cleanUp>
+      <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.TernaryOperatorCleanUp"
             id="org.eclipse.jdt.ui.cleanup.ternary_operator"
-            runAfter="org.eclipse.jdt.ui.cleanup.standard_comparison">
+            runAfter="org.eclipse.jdt.ui.cleanup.bitwise_conditional_expression">
       </cleanUp>
       <cleanUp
             class="org.eclipse.jdt.internal.ui.fix.StrictlyEqualOrDifferentCleanUp"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.java
index bc5de30..07bad14 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.java
@@ -220,6 +220,7 @@
 	public static String GenerateConstructorUsingFieldsAction_tooltip;
 	public static String GenerateConstructorUsingFieldsAction_error_title;
 	public static String GenerateConstructorUsingFieldsAction_not_applicable;
+	public static String GenerateConstructorUsingFieldsAction_record_not_applicable;
 	public static String GenerateConstructorUsingFieldsAction_fields_selected;
 	public static String GenerateConstructorUsingFieldsAction_error_duplicate_constructor;
 	public static String GenerateConstructorUsingFieldsAction_error_nothing_found;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.properties
index f0c2fc3..05fcac9 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.properties
@@ -175,6 +175,7 @@
 GenerateConstructorUsingFieldsAction_interface_not_applicable=The Generate Constructor using Fields operation is not applicable to interfaces.
 GenerateConstructorUsingFieldsAction_enum_not_applicable=The Generate Constructor using Fields operation is not applicable to enums.
 GenerateConstructorUsingFieldsAction_annotation_not_applicable=The Generate Constructor using Fields operation is not applicable to annotations.
+GenerateConstructorUsingFieldsAction_record_not_applicable=The Generate Constructor using Fields operation is not applicable to records.
 GenerateConstructorUsingFieldsAction_typeContainsNoFields_message=The selected type contains no fields which may be initialized in a constructor.
 GenerateConstructorUsingFieldsAction_error_actionfailed=Unexpected error while creating constructors. See log for details.
 GenerateConstructorUsingFieldsAction_error_anonymous_class=Anonymous classes cannot contain explicitly declared constructors.
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUp.java
new file mode 100644
index 0000000..4cf1900
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/PrimitiveComparisonCleanUp.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2021 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.Map;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
+
+import org.eclipse.jdt.ui.cleanup.CleanUpContext;
+import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
+import org.eclipse.jdt.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+
+/**
+ * A fix that replaces the <code>compareTo()</code> method by a comparison on primitive:
+ * <ul>
+ * <li>It improves the space and time performance,</li>
+ * <li>The compared value must be a primitive.</li>
+ * </ul>
+ */
+public class PrimitiveComparisonCleanUp extends AbstractCleanUp {
+	private PrimitiveComparisonCleanUpCore coreCleanUp= new PrimitiveComparisonCleanUpCore();
+
+	public PrimitiveComparisonCleanUp(final Map<String, String> options) {
+		setOptions(options);
+	}
+
+	public PrimitiveComparisonCleanUp() {
+	}
+
+	@Override
+	public void setOptions(final CleanUpOptions options) {
+		coreCleanUp.setOptions(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		return new CleanUpRequirements(coreCleanUp.getRequirementsCore());
+	}
+
+	@Override
+	public ICleanUpFix createFix(final CleanUpContext context) throws CoreException {
+		ICleanUpFixCore fixCore= coreCleanUp.createFixCore(context);
+		return fixCore != null ? new CleanUpFixWrapper(fixCore) : null;
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		return coreCleanUp.getStepDescriptions();
+	}
+
+	@Override
+	public String getPreview() {
+		return coreCleanUp.getPreview();
+	}
+}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/StaticInnerClassCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/StaticInnerClassCleanUp.java
index e05c4ca..f5a0e35 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/StaticInnerClassCleanUp.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/StaticInnerClassCleanUp.java
@@ -145,7 +145,7 @@
 							&& binding.getKind() != IBinding.TYPE) {
 						ASTNode declaration= ((CompilationUnit) root).findDeclaringNode(binding);
 
-						if (!ASTNodes.isParent(declaration, innerClass)) {
+						if (declaration == null || !ASTNodes.isParent(declaration, innerClass)) {
 							isTopLevelClassMemberUsed= true;
 							return interruptVisit();
 						}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/SubstringCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/SubstringCleanUp.java
new file mode 100644
index 0000000..03c1baa
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/SubstringCleanUp.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2021 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.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+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.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+/**
+ * A fix that removes the second <code>substring()</code> parameter if this parameter is the length of the string:
+ * <ul>
+ * <li>It must reference the same expression,</li>
+ * <li>The expression must be passive.</li>
+ * </ul>
+ */
+public class SubstringCleanUp extends AbstractMultiFix {
+	public SubstringCleanUp() {
+		this(Collections.emptyMap());
+	}
+
+	public SubstringCleanUp(final Map<String, String> options) {
+		super(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		boolean requireAST= isEnabled(CleanUpConstants.SUBSTRING);
+		return new CleanUpRequirements(requireAST, false, false, null);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		if (isEnabled(CleanUpConstants.SUBSTRING)) {
+			return new String[] { MultiFixMessages.SubstringCleanUp_description };
+		}
+
+		return new String[0];
+	}
+
+	@Override
+	public String getPreview() {
+		if (isEnabled(CleanUpConstants.SUBSTRING)) {
+			return "String shortenedText = text.substring(2);\n"; //$NON-NLS-1$
+		}
+
+		return "String shortenedText = text.substring(2, text.length());\n"; //$NON-NLS-1$
+	}
+
+	@Override
+	protected ICleanUpFix createFix(final CompilationUnit unit) throws CoreException {
+		if (!isEnabled(CleanUpConstants.SUBSTRING)) {
+			return null;
+		}
+
+		final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+		unit.accept(new ASTVisitor() {
+			@Override
+			public boolean visit(final MethodInvocation visited) {
+				if (ASTNodes.usesGivenSignature(visited, String.class.getCanonicalName(), "substring", int.class.getCanonicalName(), int.class.getCanonicalName())) { //$NON-NLS-1$
+					MethodInvocation endIndex= ASTNodes.as((Expression) visited.arguments().get(1), MethodInvocation.class);
+
+					if (endIndex != null
+							&& endIndex.getExpression() != null
+							&& ASTNodes.usesGivenSignature(endIndex, String.class.getCanonicalName(), "length") //$NON-NLS-1$
+							&& ASTNodes.match(visited.getExpression(), endIndex.getExpression())
+							&& ASTNodes.isPassive(visited.getExpression())) {
+				rewriteOperations.add(new SubstringOperation(visited));
+						return false;
+					}
+				}
+
+				return true;
+			}
+		});
+
+		if (rewriteOperations.isEmpty()) {
+			return null;
+		}
+
+		return new CompilationUnitRewriteOperationsFix(MultiFixMessages.SubstringCleanUp_description, unit,
+				rewriteOperations.toArray(new CompilationUnitRewriteOperation[0]));
+	}
+
+	@Override
+	public boolean canFix(final ICompilationUnit compilationUnit, final IProblemLocation problem) {
+		return false;
+	}
+
+	@Override
+	protected ICleanUpFix createFix(final CompilationUnit unit, final IProblemLocation[] problems) throws CoreException {
+		return null;
+	}
+
+	private static class SubstringOperation extends CompilationUnitRewriteOperation {
+		private final MethodInvocation visited;
+
+		public SubstringOperation(final MethodInvocation visited) {
+			this.visited= visited;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			TextEditGroup group= createTextEditGroup(MultiFixMessages.SubstringCleanUp_description, cuRewrite);
+
+			rewrite.remove((ASTNode) visited.arguments().get(1), group);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/UnreachableBlockCleanUp.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/UnreachableBlockCleanUp.java
new file mode 100644
index 0000000..dbb4e49
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/fix/UnreachableBlockCleanUp.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2021 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.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+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.ui.cleanup.CleanUpRequirements;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+/**
+ * A fix that detects two <code>if</code> conditions that are identical and removes the second one:
+ * <ul>
+ * <li>The conditions should be passive,</li>
+ * <li>No exceptions should be awaited,</li>
+ * <li>It should not create unreachable code below the <code>if</code> statement which would create a compile error, that is to say the case where
+ * only the removed block doesn't fall through,
+ * all the other cases fall through,
+ * there is an <code>else</code> clause (not only <code>if</code>/<code>else</code> clauses)
+ * and a statement after the control workflow.</li>
+ * </ul>
+ */
+public class UnreachableBlockCleanUp extends AbstractMultiFix {
+	public UnreachableBlockCleanUp() {
+		this(Collections.emptyMap());
+	}
+
+	public UnreachableBlockCleanUp(final Map<String, String> options) {
+		super(options);
+	}
+
+	@Override
+	public CleanUpRequirements getRequirements() {
+		boolean requireAST= isEnabled(CleanUpConstants.UNREACHABLE_BLOCK);
+		return new CleanUpRequirements(requireAST, false, false, null);
+	}
+
+	@Override
+	public String[] getStepDescriptions() {
+		if (isEnabled(CleanUpConstants.UNREACHABLE_BLOCK)) {
+			return new String[] { MultiFixMessages.UnreachableBlockCleanUp_description };
+		}
+
+		return new String[0];
+	}
+
+	@Override
+	public String getPreview() {
+		StringBuilder bld= new StringBuilder();
+		bld.append("if (isValid && isFound) {\n"); //$NON-NLS-1$
+		bld.append("    return 0;\n"); //$NON-NLS-1$
+
+		if (!isEnabled(CleanUpConstants.UNREACHABLE_BLOCK)) {
+			bld.append("} else if (isFound && isValid) {\n"); //$NON-NLS-1$
+			bld.append("    return 1;\n"); //$NON-NLS-1$
+		}
+
+		bld.append("}\n"); //$NON-NLS-1$
+
+		if (isEnabled(CleanUpConstants.UNREACHABLE_BLOCK)) {
+			bld.append("\n\n"); //$NON-NLS-1$
+		}
+
+		return bld.toString();
+	}
+
+	@Override
+	protected ICleanUpFix createFix(final CompilationUnit unit) throws CoreException {
+		if (!isEnabled(CleanUpConstants.UNREACHABLE_BLOCK)) {
+			return null;
+		}
+
+		final List<CompilationUnitRewriteOperation> rewriteOperations= new ArrayList<>();
+
+		unit.accept(new ASTVisitor() {
+			@Override
+			public boolean visit(final IfStatement visited) {
+				if (!ASTNodes.isExceptionExpected(visited)
+						&& ASTNodes.isPassive(visited.getExpression())) {
+					boolean hasNoUnreachableCodeBelow= ASTNodes.getNextStatement(visited) == null
+							|| !ASTNodes.fallsThrough(visited.getThenStatement());
+					IfStatement duplicateIf= visited;
+
+					do {
+						duplicateIf= ASTNodes.as(duplicateIf.getElseStatement(), IfStatement.class);
+
+						if (duplicateIf == null
+								|| !ASTNodes.isPassive(duplicateIf.getExpression())) {
+							return true;
+						}
+
+						if ((hasNoUnreachableCodeBelow
+								|| duplicateIf.getElseStatement() == null
+								|| ASTNodes.fallsThrough(duplicateIf.getThenStatement())
+								|| !ASTNodes.fallsThrough(duplicateIf.getElseStatement()))
+								&& ASTNodes.isPassiveWithoutFallingThrough(duplicateIf.getExpression())
+								&& ASTNodes.match(visited.getExpression(), duplicateIf.getExpression())) {
+							rewriteOperations.add(new UnreachableBlockOperation(duplicateIf));
+							return false;
+						}
+
+						hasNoUnreachableCodeBelow|= !ASTNodes.fallsThrough(duplicateIf.getThenStatement());
+					} while (true);
+				}
+
+				return true;
+			}
+		});
+
+		if (rewriteOperations.isEmpty()) {
+			return null;
+		}
+
+		return new CompilationUnitRewriteOperationsFix(MultiFixMessages.UnreachableBlockCleanUp_description, unit,
+				rewriteOperations.toArray(new CompilationUnitRewriteOperation[0]));
+	}
+
+	@Override
+	public boolean canFix(final ICompilationUnit compilationUnit, final IProblemLocation problem) {
+		return false;
+	}
+
+	@Override
+	protected ICleanUpFix createFix(final CompilationUnit unit, final IProblemLocation[] problems) throws CoreException {
+		return null;
+	}
+
+	private static class UnreachableBlockOperation extends CompilationUnitRewriteOperation {
+		private final IfStatement duplicateIf;
+
+		public UnreachableBlockOperation(final IfStatement duplicateIf) {
+			this.duplicateIf= duplicateIf;
+		}
+
+		@Override
+		public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModel linkedModel) throws CoreException {
+			ASTRewrite rewrite= cuRewrite.getASTRewrite();
+			TextEditGroup group= createTextEditGroup(MultiFixMessages.UnreachableBlockCleanUp_description, cuRewrite);
+
+			if (duplicateIf.getElseStatement() == null) {
+				rewrite.remove(duplicateIf, group);
+			} else {
+				ASTNodes.replaceButKeepComment(rewrite, duplicateIf, ASTNodes.createMoveTarget(rewrite, duplicateIf.getElseStatement()), group);
+			}
+		}
+	}
+}
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 f024959..c581eb8 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
@@ -61,7 +61,6 @@
 	public static String CodeStyleTabPage_GroupName_ControlStatements;
 
 	public static String CodeStyleTabPage_GroupName_Expressions;
-	public static String CodeStyleTabPage_CheckboxName_CheckSignOfBitwiseOperation;
 	public static String CodeStyleTabPage_CheckboxName_ExtractIncrement;
 	public static String CodeStyleTabPage_CheckboxName_PullUpAssignment;
 	public static String CodeStyleTabPage_CheckboxName_ElseIf;
@@ -87,6 +86,7 @@
 	public static String OptimizationTabPage_CheckboxName_StaticInnerClass;
 	public static String OptimizationTabPage_CheckboxName_StringBuilder;
 	public static String OptimizationTabPage_CheckboxName_UseLazyLogicalOperator;
+	public static String OptimizationTabPage_CheckboxName_PrimitiveComparison;
 	public static String OptimizationTabPage_CheckboxName_PrimitiveParsing;
 	public static String OptimizationTabPage_CheckboxName_PrimitiveSerialization;
 	public static String OptimizationTabPage_CheckboxName_PrecompileRegEx;
@@ -126,12 +126,14 @@
 
 	public static String UnnecessaryCodeTabPage_CheckboxName_UnnecessaryCasts;
 	public static String UnnecessaryCodeTabPage_CheckboxName_UnnecessaryNLSTags;
+	public static String UnnecessaryCodeTabPage_CheckboxName_Substring;
 	public static String UnnecessaryCodeTabPage_CheckboxName_ArraysFill;
 	public static String UnnecessaryCodeTabPage_CheckboxName_EvaluateNullable;
 	public static String UnnecessaryCodeTabPage_CheckboxName_PushDownNegation;
 	public static String UnnecessaryCodeTabPage_CheckboxName_DoubleNegation;
 	public static String UnnecessaryCodeTabPage_CheckboxName_ComparisonStatement;
 	public static String UnnecessaryCodeTabPage_CheckboxName_RedundantSuperCall;
+	public static String UnnecessaryCodeTabPage_CheckboxName_UnreachableBlock;
 	public static String UnnecessaryCodeTabPage_CheckboxName_UseDirectlyMapMethod;
 	public static String UnnecessaryCodeTabPage_CheckboxName_CollectionCloning;
 	public static String UnnecessaryCodeTabPage_CheckboxName_MapCloning;
@@ -156,7 +158,12 @@
 	public static String UnnecessaryCodeTabPage_GroupName_UnnecessaryCode;
 	public static String UnnecessaryCodeTabPage_GroupName_UnusedCode;
 
-	public static String CodeFixingTabPage_CheckboxName_StandardComparison;
+	public static String SourceFixingTabPage_warning;
+
+	public static String SourceFixingTabPage_GroupName_standardCode;
+
+	public static String SourceFixingTabPage_CheckboxName_StandardComparison;
+	public static String SourceFixingTabPage_CheckboxName_CheckSignOfBitwiseOperation;
 
 	public static String DuplicateCodeTabPage_GroupName_DuplicateCode;
 
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 cbce94c..46b5119 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
@@ -41,7 +41,6 @@
 CodeStyleTabPage_CheckboxName_ElseIf=C&ombine nested 'if' statement in 'else' block to 'else if'
 CodeStyleTabPage_CheckboxName_ReduceIndentation=Reduce indentation when possible
 CodeStyleTabPage_GroupName_Expressions=Expressions
-CodeStyleTabPage_CheckboxName_CheckSignOfBitwiseOperation=&Compare with != 0 for bitwise expressions (use it carefully, it may alter the behavior)
 CodeStyleTabPage_CheckboxName_ExtractIncrement=Extract increment/decrement from statement
 CodeStyleTabPage_CheckboxName_PullUpAssignment=Pull up assignment
 CodeStyleTabPage_GroupName_NumberLiteral=Number literals
@@ -66,6 +65,7 @@
 OptimizationTabPage_CheckboxName_StaticInnerClass=Make inner classes static where possible
 OptimizationTabPage_CheckboxName_StringBuilder=Replace &String concatenation by StringBuilder
 OptimizationTabPage_CheckboxName_UseLazyLogicalOperator=Use la&zy logical operator
+OptimizationTabPage_CheckboxName_PrimitiveComparison=Primitive comparison
 OptimizationTabPage_CheckboxName_PrimitiveParsing=Primitive parsing
 OptimizationTabPage_CheckboxName_PrimitiveSerialization=&Primitive serialization
 OptimizationTabPage_CheckboxName_PrecompileRegEx=Precompile reused regular e&xpressions
@@ -112,12 +112,14 @@
 UnnecessaryCodeTabPage_GroupName_UnnecessaryCode=Unnecessary code
 UnnecessaryCodeTabPage_CheckboxName_UnnecessaryCasts=Remove unnecessar&y casts
 UnnecessaryCodeTabPage_CheckboxName_UnnecessaryNLSTags=Remove unnecessary '$NON-NLS$' ta&gs
+UnnecessaryCodeTabPage_CheckboxName_Substring=Redundant String.substring() parameter
 UnnecessaryCodeTabPage_CheckboxName_ArraysFill=Use Arrays.&fill() when possible
 UnnecessaryCodeTabPage_CheckboxName_EvaluateNullable=Evaluate without null check
 UnnecessaryCodeTabPage_CheckboxName_PushDownNegation=Push d&own negation
 UnnecessaryCodeTabPage_CheckboxName_DoubleNegation=Double negation
 UnnecessaryCodeTabPage_CheckboxName_ComparisonStatement=Remove redundant comparison statement
 UnnecessaryCodeTabPage_CheckboxName_RedundantSuperCall=Remove redundant s&uper() call in constructor
+UnnecessaryCodeTabPage_CheckboxName_UnreachableBlock=Unreachable block
 UnnecessaryCodeTabPage_CheckboxName_UseDirectlyMapMethod=Operate on &Maps directly
 UnnecessaryCodeTabPage_CheckboxName_CollectionCloning=Initialize collection at creation
 UnnecessaryCodeTabPage_CheckboxName_MapCloning=Initiali&ze map at creation
@@ -133,7 +135,12 @@
 UnnecessaryCodeTabPage_CheckboxName_UselessContinue=&Remove useless continue
 UnnecessaryCodeTabPage_CheckboxName_UnloopedWhile=Convert loop into if when possible
 
-CodeFixingTabPage_CheckboxName_StandardComparison=Compare to zero
+SourceFixingTabPage_warning=These cleanups may change runtime behavior. Use them carefully.
+
+SourceFixingTabPage_GroupName_standardCode=Code standardization
+
+SourceFixingTabPage_CheckboxName_StandardComparison=Compare to zero
+SourceFixingTabPage_CheckboxName_CheckSignOfBitwiseOperation=&Compare with != 0 for bitwise expressions
 
 DuplicateCodeTabPage_GroupName_DuplicateCode=Duplicate code
 DuplicateCodeTabPage_CheckboxName_TernaryOperator=Replace (X &&&& Y) || (!X &&&& Z) by X ? Y : Z
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 d0bdee9..651592a 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
@@ -22,7 +22,6 @@
 
 import org.eclipse.jdt.internal.ui.fix.AbstractCleanUp;
 import org.eclipse.jdt.internal.ui.fix.AddAllCleanUp;
-import org.eclipse.jdt.internal.ui.fix.BitwiseConditionalExpressionCleanup;
 import org.eclipse.jdt.internal.ui.fix.ControlStatementsCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ElseIfCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ExpressionsCleanUp;
@@ -31,7 +30,6 @@
 import org.eclipse.jdt.internal.ui.fix.NumberSuffixCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PullUpAssignmentCleanUp;
 import org.eclipse.jdt.internal.ui.fix.ReduceIndentationCleanUp;
-import org.eclipse.jdt.internal.ui.fix.StandardComparisonCleanUp;
 import org.eclipse.jdt.internal.ui.fix.SwitchCleanUp;
 import org.eclipse.jdt.internal.ui.fix.VariableDeclarationCleanUp;
 
@@ -47,8 +45,6 @@
 				new ElseIfCleanUp(values),
 				new ReduceIndentationCleanUp(values),
 				new ExpressionsCleanUp(values),
-				new BitwiseConditionalExpressionCleanup(values),
-				new StandardComparisonCleanUp(values),
 				new ExtractIncrementCleanUp(values),
 				new PullUpAssignmentCleanUp(values),
 				new NumberSuffixCleanUp(values),
@@ -91,12 +87,6 @@
 		final RadioPreference useParenthesesNeverPref= createRadioPref(expressionsGroup, 1, CleanUpMessages.CodeStyleTabPage_RadioName_NeverUseParantheses, CleanUpConstants.EXPRESSIONS_USE_PARENTHESES_NEVER, CleanUpModifyDialog.FALSE_TRUE);
 		registerSlavePreference(useParenthesesPref, new RadioPreference[] {useParenthesesAlwaysPref, useParenthesesNeverPref});
 
-		final CheckboxPreference bitwiseComparison= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_CheckSignOfBitwiseOperation, CleanUpConstants.CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpModifyDialog.FALSE_TRUE);
-		registerPreference(bitwiseComparison);
-
-		final CheckboxPreference standardComparisonPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeFixingTabPage_CheckboxName_StandardComparison, CleanUpConstants.STANDARD_COMPARISON, CleanUpModifyDialog.FALSE_TRUE);
-		registerPreference(standardComparisonPref);
-
 		final CheckboxPreference extractIncrementPref= createCheckboxPref(expressionsGroup, numColumns, CleanUpMessages.CodeStyleTabPage_CheckboxName_ExtractIncrement, CleanUpConstants.EXTRACT_INCREMENT, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(extractIncrementPref);
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java
index a166f7a..ca79c45 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/OptimizationTabPage.java
@@ -26,10 +26,11 @@
 import org.eclipse.jdt.internal.ui.fix.LazyLogicalCleanUp;
 import org.eclipse.jdt.internal.ui.fix.NoStringCreationCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PatternCleanUp;
+import org.eclipse.jdt.internal.ui.fix.PrimitiveComparisonCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PrimitiveParsingCleanUp;
 import org.eclipse.jdt.internal.ui.fix.PrimitiveSerializationCleanUp;
-import org.eclipse.jdt.internal.ui.fix.StaticInnerClassCleanUp;
 import org.eclipse.jdt.internal.ui.fix.SingleUsedFieldCleanUp;
+import org.eclipse.jdt.internal.ui.fix.StaticInnerClassCleanUp;
 import org.eclipse.jdt.internal.ui.fix.StringBuilderCleanUp;
 
 public final class OptimizationTabPage extends AbstractCleanUpTabPage {
@@ -43,6 +44,7 @@
 				new StaticInnerClassCleanUp(values),
 				new StringBuilderCleanUp(values),
 				new LazyLogicalCleanUp(values),
+				new PrimitiveComparisonCleanUp(values),
 				new PrimitiveParsingCleanUp(values),
 				new PrimitiveSerializationCleanUp(values),
 				new PatternCleanUp(values),
@@ -71,6 +73,9 @@
 				CleanUpConstants.USE_LAZY_LOGICAL_OPERATOR, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(useLazyLogicalPref);
 
+		final CheckboxPreference primitiveComparisonPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_PrimitiveComparison, CleanUpConstants.PRIMITIVE_COMPARISON, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(primitiveComparisonPref);
+
 		final CheckboxPreference primitiveParsingPref= createCheckboxPref(optimizationGroup, numColumns, CleanUpMessages.OptimizationTabPage_CheckboxName_PrimitiveParsing, CleanUpConstants.PRIMITIVE_PARSING, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(primitiveParsingPref);
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/SourceFixingTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/SourceFixingTabPage.java
new file mode 100644
index 0000000..096e5ef
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/SourceFixingTabPage.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2021 IBM Corporation 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.preferences.cleanup;
+
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+
+import org.eclipse.jface.dialogs.Dialog;
+
+import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
+
+import org.eclipse.jdt.internal.ui.fix.AbstractCleanUp;
+import org.eclipse.jdt.internal.ui.fix.BitwiseConditionalExpressionCleanup;
+import org.eclipse.jdt.internal.ui.fix.StandardComparisonCleanUp;
+
+public final class SourceFixingTabPage extends AbstractCleanUpTabPage {
+	public static final String ID= "org.eclipse.jdt.ui.cleanup.tabpage.source_fixing"; //$NON-NLS-1$
+
+	@Override
+	protected AbstractCleanUp[] createPreviewCleanUps(final Map<String, String> values) {
+		return new AbstractCleanUp[] {
+				new StandardComparisonCleanUp(values),
+				new BitwiseConditionalExpressionCleanup(values)
+		};
+	}
+
+	@Override
+	protected void doCreatePreferences(final Composite composite, final int numColumns) {
+		final Label warningImage= new Label(composite, SWT.LEFT | SWT.WRAP);
+		warningImage.setImage(Dialog.getImage(Dialog.DLG_IMG_MESSAGE_WARNING));
+		warningImage.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, false, false));
+		createLabel(numColumns - 1, composite, CleanUpMessages.SourceFixingTabPage_warning);
+
+		Group standardCodeGroup= createGroup(numColumns, composite, CleanUpMessages.SourceFixingTabPage_GroupName_standardCode);
+
+		final CheckboxPreference standardComparisonPref= createCheckboxPref(standardCodeGroup, numColumns, CleanUpMessages.SourceFixingTabPage_CheckboxName_StandardComparison, CleanUpConstants.STANDARD_COMPARISON, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(standardComparisonPref);
+
+		final CheckboxPreference bitwiseComparisonPref= createCheckboxPref(standardCodeGroup, numColumns, CleanUpMessages.SourceFixingTabPage_CheckboxName_CheckSignOfBitwiseOperation, CleanUpConstants.CHECK_SIGN_OF_BITWISE_OPERATION, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(bitwiseComparisonPref);
+	}
+}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java
index ddce1ca..2219d66 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/cleanup/UnnecessaryCodeTabPage.java
@@ -36,9 +36,11 @@
 import org.eclipse.jdt.internal.ui.fix.RedundantSemicolonsCleanUp;
 import org.eclipse.jdt.internal.ui.fix.RedundantSuperCallCleanUp;
 import org.eclipse.jdt.internal.ui.fix.StringCleanUp;
+import org.eclipse.jdt.internal.ui.fix.SubstringCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UnloopedWhileCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UnnecessaryArrayCreationCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UnnecessaryCodeCleanUp;
+import org.eclipse.jdt.internal.ui.fix.UnreachableBlockCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UselessContinueCleanUp;
 import org.eclipse.jdt.internal.ui.fix.UselessReturnCleanUp;
@@ -51,6 +53,7 @@
 		return new AbstractCleanUp[] {
 				new UnusedCodeCleanUp(values),
 				new UnnecessaryCodeCleanUp(values),
+				new SubstringCleanUp(values),
 				new StringCleanUp(values),
 				new ArraysFillCleanUp(values),
 				new EvaluateNullableCleanUp(values),
@@ -58,6 +61,7 @@
 				new DoubleNegationCleanUp(values),
 				new RedundantComparisonStatementCleanUp(values),
 				new RedundantSuperCallCleanUp(values),
+				new UnreachableBlockCleanUp(values),
 				new MapMethodCleanUp(values),
 				new CollectionCloningCleanUp(values),
 				new MapCloningCleanUp(values),
@@ -98,6 +102,9 @@
     	CheckboxPreference nls= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_UnnecessaryNLSTags, CleanUpConstants.REMOVE_UNNECESSARY_NLS_TAGS, CleanUpModifyDialog.FALSE_TRUE);
     	registerPreference(nls);
 
+		CheckboxPreference substring= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_Substring, CleanUpConstants.SUBSTRING, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(substring);
+
 		CheckboxPreference arraysFill= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_ArraysFill, CleanUpConstants.ARRAYS_FILL, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(arraysFill);
 
@@ -117,6 +124,9 @@
 		CheckboxPreference redundantSuperCall= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_RedundantSuperCall, CleanUpConstants.REDUNDANT_SUPER_CALL, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(redundantSuperCall);
 
+		CheckboxPreference unreachableBlock= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_UnreachableBlock, CleanUpConstants.UNREACHABLE_BLOCK, CleanUpModifyDialog.FALSE_TRUE);
+		registerPreference(unreachableBlock);
+
 		CheckboxPreference mapMethod= createCheckboxPref(unnecessaryGroup, numColumns, CleanUpMessages.UnnecessaryCodeTabPage_CheckboxName_UseDirectlyMapMethod,
 				CleanUpConstants.USE_DIRECTLY_MAP_METHOD, CleanUpModifyDialog.FALSE_TRUE);
 		registerPreference(mapMethod);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ConvertStringConcatenationProposals.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ConvertStringConcatenationProposals.java
index f5e1029..90cbed2 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ConvertStringConcatenationProposals.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/ConvertStringConcatenationProposals.java
@@ -284,7 +284,7 @@
 			if (operand instanceof StringLiteral) {
 				String value= ((StringLiteral) operand).getEscapedValue();
 				value= value.substring(1, value.length() - 1);
-				value= value.replaceAll("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$
+				value= value.replace("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$
 				formatString.append(value);
 			} else {
 				formatString.append("{").append(i).append("}"); //$NON-NLS-1$ //$NON-NLS-2$
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/MethodProposalInfo.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/MethodProposalInfo.java
index 2f4e00d..2ce106c 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/MethodProposalInfo.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/MethodProposalInfo.java
@@ -218,7 +218,7 @@
 	private String computeSimpleTypeName(String signature, Map<String, char[]> typeVariables) {
 		// method equality uses erased types
 		String erasure= Signature.getTypeErasure(signature);
-		erasure= erasure.replaceAll("/", ".");  //$NON-NLS-1$//$NON-NLS-2$
+		erasure= erasure.replace('/', '.');
 		String simpleName= Signature.getSimpleName(Signature.toString(erasure));
 		char[] typeVar= typeVariables.get(simpleName);
 		if (typeVar != null)
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
index 195b162..92fa933 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
@@ -758,7 +758,7 @@
 			return Strings.markJavaElementLabelLTR(buf.toString());
 		} else {
 			String label= JavaElementLabels.getElementLabel(element, flags);
-			return label.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			return label.replace("<", "&lt;").replace(">", "&gt;"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 		}
 	}
 
@@ -781,7 +781,7 @@
 			return Strings.markJavaElementLabelLTR(buf.toString());
 		} else {
 			String label= JavaElementLabels.getElementLabel(element, flags);
-			return label.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			return label.replace("<", "&lt;").replace(">", "&gt;"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 		}
 	}
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java
index 7c2b25b..c468938 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java
@@ -134,7 +134,7 @@
 
 		if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
 			IType type= (IType) selection.getFirstElement();
-			return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation() && !type.isAnonymous();
+			return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation() && !type.isAnonymous() && !type.isRecord();
 		}
 
 		if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
@@ -236,20 +236,27 @@
 			}
 			Object firstElement= selection.getFirstElement();
 
+			IType typeToProcess= null;
 			if (firstElement instanceof IType) {
-				run((IType) firstElement, new IField[0], false);
+				typeToProcess= (IType)firstElement;
 			} else if (firstElement instanceof ICompilationUnit) {
-				IType type= ((ICompilationUnit) firstElement).findPrimaryType();
-				if (type.isAnnotation()) {
+				typeToProcess= ((ICompilationUnit) firstElement).findPrimaryType();
+			}
+			if (typeToProcess != null) {
+				if (typeToProcess.isAnnotation()) {
 					MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_annotation_not_applicable);
 					notifyResult(false);
 					return;
-				} else if (type.isInterface()) {
+				} else if (typeToProcess.isInterface()) {
 					MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_interface_not_applicable);
 					notifyResult(false);
 					return;
+				} else if (typeToProcess.isRecord()) {
+					MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_record_not_applicable);
+					notifyResult(false);
+					return;
 				} else
-					run(((ICompilationUnit) firstElement).findPrimaryType(), new IField[0], false);
+					run(typeToProcess, new IField[0], false);
 			}
 		} catch (CoreException exception) {
 			ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
@@ -273,7 +280,9 @@
 			if (element != null) {
 				IType type= (IType) element.getAncestor(IJavaElement.TYPE);
 				if (type != null) {
-					if (type.getFields().length > 0) {
+					if (type.isRecord()) {
+						MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_record_not_applicable);
+					} else if (type.getFields().length > 0) {
 						run(type, new IField[0], true);
 					} else {
 						MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
diff --git a/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF b/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF
index d053a45..7b7a642 100644
--- a/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF
+++ b/org.eclipse.ltk.core.refactoring/META-INF/MANIFEST.MF
@@ -21,7 +21,7 @@
  org.eclipse.core.expressions;bundle-version="[3.4.100,4.0.0)",
  org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)",
  org.eclipse.core.filebuffers;bundle-version="[3.5.0,4.0.0)",
- org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.14.0,4.0.0)",
  org.eclipse.core.commands;bundle-version="[3.5.0,4.0.0)",
  org.eclipse.text;bundle-version="[3.5.0,4.0.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/MarkerUndoState.java b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/MarkerUndoState.java
index b8d972f..83ed85a 100644
--- a/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/MarkerUndoState.java
+++ b/org.eclipse.ltk.core.refactoring/src/org/eclipse/ltk/internal/core/refactoring/resource/undostates/MarkerUndoState.java
@@ -35,7 +35,7 @@
 	protected IResource resource;
 
 	private String type;
-	private Map<String, ? extends Object> attributes;
+	private Map<String, Object> attributes;
 
 	/**
 	 *
@@ -53,31 +53,13 @@
 	}
 
 	/**
-	 * Create a {@link MarkerUndoState} from the specified marker type, attributes,
-	 * and resource.
-	 *
-	 * @param type
-	 *            the type of marker to be created.
-	 * @param attributes
-	 *            the attributes to be assigned to the marker
-	 * @param resource
-	 *            the resource on which the marker should be created
-	 */
-	public MarkerUndoState(String type, Map<String, ? extends Object> attributes, IResource resource) {
-		this.type = type;
-		this.attributes = attributes;
-		this.resource = resource;
-	}
-
-	/**
 	 * Create a marker from the marker description.
 	 *
 	 * @return the created marker
 	 * @throws CoreException if the marker could not be created
 	 */
 	public IMarker createMarker() throws CoreException {
-		IMarker marker = resource.createMarker(type);
-		marker.setAttributes(attributes);
+		IMarker marker = resource.createMarker(type, attributes);
 		return marker;
 	}