Bug 573884 - [refactoring] "Extract superclass" produces compile errors

- make PullUpRefactoringProcessor.checkFinalFields() method protected
  so it can be overridden
- add new checkFinalFields() method to ExtractSupertypeProcessor
  that doesn't do anything and succeeds
- add new code to ExtractSupertypeProcessor.createNecessaryConstructors
  method to keep track of any uninitialized non-static final fields
  that are being moved and to modify the constructors to pass and
  set these fields
- add new fixConstructorsWithFinalFieldInitialization to modify
  any constructors in the original type that initialize final fields
  that are moved to add the field arguments to a super constructor
  and remove any statements that set the fields
- add new tests to ExtractSupertypeTests and add support for
  extracting fields as well as methods

Change-Id: I9592e0d9a01d764531a88837cdb7a04ac23b30f0
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.ui/+/183208
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Tested-by: Jeff Johnston <jjohnstn@redhat.com>
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/in/A.java
new file mode 100644
index 0000000..c83bbc8
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/in/A.java
@@ -0,0 +1,17 @@
+package p;
+class A{
+}
+class B extends A{
+	public final int a;
+	public final int b;
+	public int geta() {
+		return a;
+	}
+	public int getb() {
+		return b;
+	}
+	public B(int a, int b) {
+		this.a = a;
+		this.b = b;
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/A.java
new file mode 100644
index 0000000..7dced99
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/A.java
@@ -0,0 +1,8 @@
+package p;
+class A{
+}
+class B extends Z{
+	public B(int a, int b) {
+		super(a, b);
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/Z.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/Z.java
new file mode 100644
index 0000000..b9173e6
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_1/out/Z.java
@@ -0,0 +1,19 @@
+package p;
+public class Z extends A {
+	public final int a;
+	public final int b;
+
+	public Z(int a, int b) {
+		super();
+		this.a = a;
+		this.b = b;
+	}
+
+	public int geta() {
+		return a;
+	}
+
+	public int getb() {
+		return b;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/in/A.java
new file mode 100644
index 0000000..471b828
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/in/A.java
@@ -0,0 +1,22 @@
+package p;
+class A{
+	private int a;
+	public A(int a) {
+		this.a = a;
+	}
+}
+class B extends A{
+	public final int a;
+	public final int b;
+	public int geta() {
+		return a;
+	}
+	public int getb() {
+		return b;
+	}
+	public B(int a, int b) {
+		super(4);
+		this.a = a;
+		this.b = b;
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/A.java
new file mode 100644
index 0000000..8a86deb
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/A.java
@@ -0,0 +1,12 @@
+package p;
+class A{
+	private int a;
+	public A(int a) {
+		this.a = a;
+	}
+}
+class B extends Z{
+	public B(int a, int b) {
+		super(4, a, b);
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/Z.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/Z.java
new file mode 100644
index 0000000..eb258d6
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_2/out/Z.java
@@ -0,0 +1,19 @@
+package p;
+public class Z extends A {
+	public final int a;
+	public final int b;
+
+	public Z(int a, int a_2, int b) {
+		super(a);
+		this.a = a_2;
+		this.b = b;
+	}
+
+	public int geta() {
+		return a;
+	}
+
+	public int getb() {
+		return b;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/in/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/in/A.java
new file mode 100644
index 0000000..581c69c
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/in/A.java
@@ -0,0 +1,22 @@
+package p;
+class A{
+	private int a;
+	public A(int a) {
+		this.a = a;
+	}
+}
+class B extends A{
+	public final int a;
+	public final boolean b;
+	public int geta() {
+		return a;
+	}
+	public boolean getb() {
+		return b;
+	}
+	public B(int k, boolean l) {
+		super(4);
+		a = k;
+		b = l;
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/A.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/A.java
new file mode 100644
index 0000000..a9d39fd
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/A.java
@@ -0,0 +1,12 @@
+package p;
+class A{
+	private int a;
+	public A(int a) {
+		this.a = a;
+	}
+}
+class B extends Z{
+	public B(int k, boolean l) {
+		super(4, k, l);
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/Z.java b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/Z.java
new file mode 100644
index 0000000..b8077fb
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/ExtractSupertype/testBug573884_3/out/Z.java
@@ -0,0 +1,19 @@
+package p;
+public class Z extends A {
+	public final int a;
+	public final boolean b;
+
+	public Z(int a, int a_2, boolean b) {
+		super(a);
+		this.a = a_2;
+		this.b = b;
+	}
+
+	public int geta() {
+		return a;
+	}
+
+	public boolean getb() {
+		return b;
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractSupertypeTests.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractSupertypeTests.java
index c535ab3..9a13d8a 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractSupertypeTests.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/ExtractSupertypeTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2020 IBM Corporation and others.
+ * Copyright (c) 2000, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -28,6 +28,7 @@
 import org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring;
 
 import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IMember;
@@ -82,24 +83,27 @@
 		return REFACTORING_PATH;
 	}
 
-	private void helper1(String[] methodNames, String[][] signatures, boolean deleteAllInSourceType, boolean deleteAllMatchingMethods, boolean replaceOccurences) throws Exception {
+	private void helper1(String[] methodNames, String[][] signatures, String[] fieldNames, boolean deleteAllInSourceType, boolean deleteAllMatchingMethods, boolean replaceOccurences) throws Exception {
 		ICompilationUnit cu= createCUfromTestFile(getPackageP(), "A");
 		IType type= getType(cu, "B");
 		IMethod[] methods= getMethods(type, methodNames, signatures);
 
-		ExtractSupertypeProcessor processor= createRefactoringProcessor(methods);
+		IField[] fields= getFields(type, fieldNames);
+
+		IMember[] members= merge(methods, fields);
+		ExtractSupertypeProcessor processor= createRefactoringProcessor(members);
 		Refactoring refactoring= processor.getRefactoring();
-		processor.setMembersToMove(methods);
 
 		assertTrue("activation", refactoring.checkInitialConditions(new NullProgressMonitor()).isOK());
 
+		processor.setMembersToMove(members);
 		processor.setTypesToExtract(new IType[] { type});
 		processor.setTypeName("Z");
-		processor.setCreateMethodStubs(true);
+		processor.setCreateMethodStubs(false);
 		processor.setInstanceOf(false);
 		processor.setReplace(replaceOccurences);
 		if (deleteAllInSourceType)
-			processor.setDeletedMethods(methods);
+			processor.setDeletedMethods(getMethods(members));
 		if (deleteAllMatchingMethods)
 			processor.setDeletedMethods(getMethods(processor.getMatchingElements(new NullProgressMonitor(), false)));
 
@@ -108,49 +112,65 @@
 		performChange(refactoring, false);
 
 		String expected= getFileContents(getOutputTestFileName("A"));
-		String actual= cu.getSource();
+		ICompilationUnit unit= getPackageP().getCompilationUnit("A.java");
+		String actual= unit.getBuffer().getContents();
 		assertEqualLines(expected, actual);
 
 		expected= getFileContents(getOutputTestFileName("Z"));
-		ICompilationUnit unit= getPackageP().getCompilationUnit("Z.java");
+		unit= getPackageP().getCompilationUnit("Z.java");
 		assertTrue("extracted compilation unit does not exist", unit.exists());
 		actual= unit.getBuffer().getContents();
 		assertEqualLines(expected, actual);
-
 	}
 
 	@Test
 	public void test0() throws Exception {
-		helper1(new String[] { "m"}, new String[][] { new String[0]}, true, false, true);
+		helper1(new String[] { "m"}, new String[][] { new String[0]}, null, true, false, true);
 	}
 
 	@Test
 	public void test1() throws Exception {
-		helper1(new String[] { "m"}, new String[][] { new String[0]}, true, false, true);
+		helper1(new String[] { "m"}, new String[][] { new String[0]}, null, true, false, true);
 	}
 
 	@Test
 	public void test2() throws Exception {
-		helper1(new String[] { "m", "n"}, new String[][] { new String[0], new String[0]}, true, false, true);
+		helper1(new String[] { "m", "n"}, new String[][] { new String[0], new String[0]}, null, true, false, true);
 	}
 
 	@Test
 	public void test3() throws Exception {
-		helper1(new String[] { "m", "n"}, new String[][] { new String[0], new String[0]}, true, false, true);
+		helper1(new String[] { "m", "n"}, new String[][] { new String[0], new String[0]}, null, true, false, true);
 	}
 
 	@Test
 	public void test4() throws Exception {
-		helper1(new String[] { "m"}, new String[][] { new String[0]}, true, false, true);
+		helper1(new String[] { "m"}, new String[][] { new String[0]}, null, true, false, true);
 	}
 
 	@Test
 	public void testBug151683() throws Exception {
-		helper1(new String[] { "m" }, new String[][] { new String[0] }, true, false, false);
+		helper1(new String[] { "m" }, new String[][] { new String[0] }, null, true, false, false);
 	}
 
 	@Test
 	public void testBug240353() throws Exception {
-		helper1(new String[] { "foo" }, new String[][] { new String[] { Signature.createTypeSignature("T", false) } }, true, false, false);
+		helper1(new String[] { "foo" }, new String[][] { new String[] { Signature.createTypeSignature("T", false) } },
+				null, true, false, false);
+	}
+
+	@Test
+	public void testBug573884_1() throws Exception {
+		helper1(new String[] { "geta", "getb" }, new String[][] { new String[0], new String[0] }, new String[] { "a", "b" }, true, false, true);
+	}
+
+	@Test
+	public void testBug573884_2() throws Exception {
+		helper1(new String[] { "geta", "getb" }, new String[][] { new String[0], new String[0] }, new String[] { "a", "b" }, true, false, true);
+	}
+
+	@Test
+	public void testBug573884_3() throws Exception {
+		helper1(new String[] { "geta", "getb" }, new String[][] { new String[0], new String[0] }, new String[] { "a", "b" }, true, false, true);
 	}
 }
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ExtractSupertypeProcessor.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ExtractSupertypeProcessor.java
index d8d9e4b..fc97aeb 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ExtractSupertypeProcessor.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ExtractSupertypeProcessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2018 IBM Corporation and others.
+ * Copyright (c) 2006, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -53,6 +53,7 @@
 
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IMember;
@@ -64,18 +65,34 @@
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTParser;
 import org.eclipse.jdt.core.dom.ASTRequestor;
+import org.eclipse.jdt.core.dom.ASTVisitor;
 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
 import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
 import org.eclipse.jdt.core.dom.IBinding;
 import org.eclipse.jdt.core.dom.IMethodBinding;
 import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.Modifier;
 import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
 import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
 import org.eclipse.jdt.core.formatter.CodeFormatter;
@@ -92,6 +109,7 @@
 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2Core;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
 import org.eclipse.jdt.internal.corext.refactoring.Checks;
 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
@@ -154,6 +172,26 @@
 	/** The types where to extract the supertype */
 	private IType[] fTypesToExtract= {};
 
+	private class FieldInfo {
+		private ITypeBinding fieldBinding;
+		private String fieldName;
+
+		public FieldInfo(ITypeBinding fieldBinding, String fieldName) {
+			this.fieldBinding= fieldBinding;
+			this.fieldName= fieldName;
+		}
+
+		public ITypeBinding getFieldBinding() {
+			return fieldBinding;
+		}
+
+		public String getFieldName() {
+			return fieldName;
+		}
+	}
+
+	private List<FieldInfo> fUninitializedFinalFieldsToMove= new ArrayList<>();
+
 	/**
 	 * Creates a new extract supertype refactoring processor.
 	 *
@@ -358,7 +396,7 @@
 				final String name= JavaModelUtil.getRenamedCUName(declaring.getCompilationUnit(), fTypeName);
 				final ICompilationUnit original= declaring.getPackageFragment().getCompilationUnit(name);
 				final ICompilationUnit copy= getSharedWorkingCopy(original.getPrimary(), new SubProgressMonitor(monitor, 10));
-				fSuperSource= createSuperTypeSource(copy, superType, declaringDeclaration, status, new SubProgressMonitor(monitor, 10));
+				fSuperSource= createSuperTypeSource(copy, superType, declaringDeclaration, declaringRewrite, status, new SubProgressMonitor(monitor, 10));
 				if (fSuperSource != null) {
 					copy.getBuffer().setContents(fSuperSource);
 					JavaModelUtil.reconcile(copy);
@@ -423,12 +461,18 @@
 	 *            <code>java.lang.Object</code>) is available
 	 * @param targetDeclaration
 	 *            the type declaration of the target type
+	 * @param declaringDeclaration
+	 *            the type declaration of the source type
+	 * @param declaringRewrite
+	 *            the CompilationUnitRewrite for the source type
 	 * @param status
 	 *            the refactoring status
 	 */
-	protected void createNecessaryConstructors(final CompilationUnitRewrite targetRewrite, final IType superType, final AbstractTypeDeclaration targetDeclaration, final RefactoringStatus status) {
+	protected void createNecessaryConstructors(final CompilationUnitRewrite targetRewrite, final IType superType,
+			final AbstractTypeDeclaration targetDeclaration, final AbstractTypeDeclaration declaringDeclaration, CompilationUnitRewrite declaringRewrite, final RefactoringStatus status) {
 		Assert.isNotNull(targetRewrite);
 		Assert.isNotNull(targetDeclaration);
+		fUninitializedFinalFieldsToMove.clear();
 		if (superType != null) {
 			final ITypeBinding binding= targetDeclaration.resolveBinding();
 			if (binding != null && binding.isClass()) {
@@ -441,6 +485,34 @@
 				}
 				final ListRewrite rewrite= targetRewrite.getASTRewrite().getListRewrite(targetDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
 				if (rewrite != null) {
+					List<BodyDeclaration> members= declaringDeclaration.bodyDeclarations();
+					ImportRewrite importRewrite= targetRewrite.getImportRewrite();
+					for (BodyDeclaration member : members) {
+						if (member instanceof FieldDeclaration) {
+							FieldDeclaration fieldDecl= (FieldDeclaration)member;
+							Type fieldType= fieldDecl.getType();
+							ITypeBinding fieldTypeBinding= fieldType.resolveBinding();
+							if (fieldTypeBinding == null) {
+								continue;
+							}
+							int modifiers= fieldDecl.getModifiers();
+							if (Modifier.isFinal(modifiers) && !Modifier.isStatic(modifiers)) {
+								List<VariableDeclarationFragment> fragments= fieldDecl.fragments();
+								for (VariableDeclarationFragment fragment : fragments) {
+									for (IMember memberToMove : fMembersToMove) {
+										if (memberToMove instanceof IField) {
+											String name= memberToMove.getElementName();
+											if (name.equals(fragment.getName().getFullyQualifiedName())) {
+												if (fragment.getInitializer() == null) {
+													fUninitializedFinalFieldsToMove.add(new FieldInfo(fieldTypeBinding, name));
+												}
+											}
+										}
+									}
+								}
+							}
+						}
+					}
 					boolean createDeprecated= deprecationCount == bindings.length;
 					for (IMethodBinding curr : bindings) {
 						if (!curr.isDeprecated() || createDeprecated) {
@@ -449,8 +521,24 @@
 								ImportRewriteContext context= new ContextSensitiveImportRewriteContext(targetDeclaration, targetRewrite.getImportRewrite());
 								stub= StubUtility2.createConstructorStub(targetRewrite.getCu(), targetRewrite.getASTRewrite(), targetRewrite.getImportRewrite(), context, curr, binding.getName(),
 									Modifier.PUBLIC, false, false, fSettings);
-								if (stub != null)
+								if (stub != null) {
+									if (!fUninitializedFinalFieldsToMove.isEmpty()) {
+										ASTRewrite astRewrite= targetRewrite.getASTRewrite();
+										List<SingleVariableDeclaration> newParms= getNewConstructorParms(stub, fUninitializedFinalFieldsToMove, importRewrite);
+										stub.parameters().addAll(newParms);
+										Block body= stub.getBody();
+										ListRewrite bodyRewrite= astRewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY);
+										for (int i= 0; i < fUninitializedFinalFieldsToMove.size(); ++i) {
+											FieldInfo finalField= fUninitializedFinalFieldsToMove.get(i);
+											SingleVariableDeclaration parm= newParms.get(i);
+											String name= finalField.getFieldName();
+											String buffer= "this." + name + " = " + parm.getName().getFullyQualifiedName() + ";"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+											ExpressionStatement assignment= (ExpressionStatement)astRewrite.createStringPlaceholder(buffer, ASTNode.EXPRESSION_STATEMENT);
+											bodyRewrite.insertLast(assignment, null);
+										}
+									}
 									rewrite.insertLast(stub, null);
+								}
 							} catch (CoreException exception) {
 								JavaPlugin.log(exception);
 								status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractSupertypeProcessor_unexpected_exception_on_layer));
@@ -462,6 +550,150 @@
 		}
 	}
 
+	private List<SingleVariableDeclaration> getNewConstructorParms(MethodDeclaration stub,
+			List<FieldInfo> uninitializedFinalFieldsToMove, ImportRewrite importRewrite) {
+		List<SingleVariableDeclaration> result= new ArrayList<>();
+		Set<String> usedFieldNames= new HashSet<>();
+		Set<String> usedFinalFieldNames= new HashSet<>();
+		List<SingleVariableDeclaration> params= stub.parameters();
+		for (SingleVariableDeclaration param : params) {
+			usedFieldNames.add(param.getName().getFullyQualifiedName());
+		}
+		for (FieldInfo fieldInfo : uninitializedFinalFieldsToMove) {
+			String name= fieldInfo.getFieldName();
+			usedFinalFieldNames.add(name);
+		}
+		for (FieldInfo fieldInfo : uninitializedFinalFieldsToMove) {
+			String name= fieldInfo.getFieldName();
+			if (usedFieldNames.contains(name)) {
+				String newName= name;
+				int i= 2;
+				do {
+					newName= name + "_" + i++; //$NON-NLS-1$
+				} while (usedFieldNames.contains(newName) || usedFinalFieldNames.contains(newName));
+				name= newName;
+			}
+			AST ast= stub.getAST();
+			SingleVariableDeclaration newParam= ast.newSingleVariableDeclaration();
+			newParam.setName(ast.newSimpleName(name));
+			newParam.setType(importRewrite.addImport(fieldInfo.getFieldBinding(), ast));
+			result.add(newParam);
+		}
+		return result;
+	}
+
+	@Override
+	public RefactoringStatus checkFinalFields(IProgressMonitor monitor) {
+		final RefactoringStatus result= new RefactoringStatus();
+		monitor.done();
+		return result;
+	}
+
+	private void fixConstructorsWithFinalFieldInitialization(CompilationUnitRewrite rewrite) {
+		ASTRewrite rewriter= rewrite.getASTRewrite();
+		CompilationUnit cu= rewrite.getRoot();
+		ASTVisitor visitor= new ASTVisitor() {
+			MethodDeclaration currentConstructor= null;
+			SuperConstructorInvocation currentSuperCall= null;
+			boolean constructorInvocation= false;
+			Expression[] fRightSide= new Expression[fUninitializedFinalFieldsToMove.size()];
+			@Override
+			public boolean visit(MethodDeclaration node) {
+				IMethodBinding binding= node.resolveBinding();
+				if (binding != null && node.isConstructor() && binding.getDeclaringClass().getQualifiedName().equals(getDeclaringType().getFullyQualifiedName())) {
+					currentConstructor= node;
+					return true;
+				}
+				return false;
+			}
+
+			@Override
+			public boolean visit(SuperConstructorInvocation node) {
+				currentSuperCall= node;
+				return false;
+			}
+
+			@Override
+			public boolean visit(ConstructorInvocation node) {
+				constructorInvocation= true;
+				return false;
+			}
+
+			@Override
+			public void endVisit(MethodDeclaration node) {
+				if (!constructorInvocation) {
+					if (currentConstructor != null) {
+						Block constructorBody= currentConstructor.getBody();
+						ListRewrite constructorRewrite= rewriter.getListRewrite(constructorBody, Block.STATEMENTS_PROPERTY);
+						AST ast= cu.getAST();
+						if (currentSuperCall != null) {
+							SuperConstructorInvocation newSuperCall= ast.newSuperConstructorInvocation();
+							List<Expression> oldArguments= currentSuperCall.arguments();
+							for (Expression argument : oldArguments) {
+								newSuperCall.arguments().add(rewriter.createCopyTarget(argument));
+							}
+							for (Expression exp : fRightSide) {
+								if (exp == null) {
+									return;
+								}
+								Expression newExp= (Expression)rewriter.createCopyTarget(exp);
+								newSuperCall.arguments().add(newExp);
+								Statement stmt= (Statement)ASTNodes.getFirstAncestorOrNull(exp, Statement.class);
+								constructorRewrite.remove(stmt, null);
+							}
+							constructorRewrite.replace(currentSuperCall, newSuperCall, null);
+						} else {
+							SuperConstructorInvocation newSuperCall= ast.newSuperConstructorInvocation();
+							for (Expression exp : fRightSide) {
+								if (exp == null) {
+									return;
+								}
+								Expression newExp= (Expression)rewriter.createCopyTarget(exp);
+								newSuperCall.arguments().add(newExp);
+								Statement stmt= (Statement)ASTNodes.getFirstAncestorOrNull(exp, Statement.class);
+								constructorRewrite.remove(stmt, null);
+							}
+							constructorRewrite.insertFirst(newSuperCall, null);
+						}
+					}
+				}
+				currentConstructor= null;
+				currentSuperCall= null;
+				constructorInvocation= false;
+			}
+
+			@Override
+			public boolean visit(Assignment node) {
+				if (currentConstructor != null) {
+					Expression leftSide= node.getLeftHandSide();
+					if (leftSide instanceof FieldAccess) {
+						FieldAccess f= (FieldAccess)leftSide;
+						for (int i= 0; i < fUninitializedFinalFieldsToMove.size(); ++i) {
+							if (fUninitializedFinalFieldsToMove.get(i).getFieldName().equals(f.getName().getFullyQualifiedName())) {
+								fRightSide[i]= node.getRightHandSide();
+							}
+						}
+					} else if (leftSide instanceof SimpleName) {
+						IBinding binding= ((SimpleName)leftSide).resolveBinding();
+						if (binding instanceof IVariableBinding) {
+							IVariableBinding varBinding= (IVariableBinding)binding;
+							if (varBinding.isField() && varBinding.getDeclaringClass().getQualifiedName().equals(getDeclaringType().getFullyQualifiedName())) {
+								String fieldName= ((SimpleName)leftSide).getFullyQualifiedName();
+								for (int i= 0; i < fUninitializedFinalFieldsToMove.size(); ++i) {
+									if (fUninitializedFinalFieldsToMove.get(i).getFieldName().equals(fieldName)) {
+										fRightSide[i]= node.getRightHandSide();
+									}
+								}
+							}
+						}
+					}
+				}
+				return false;
+			}
+		};
+		cu.accept(visitor);
+	}
+
 	/**
 	 * Creates the source for the new compilation unit containing the supertype.
 	 *
@@ -472,6 +704,7 @@
 	 *            <code>java.lang.Object</code>) is available
 	 * @param declaringDeclaration
 	 *            the declaration of the declaring type
+	 * @param declaringRewrite
 	 * @param status
 	 *            the refactoring status
 	 * @param monitor
@@ -480,7 +713,7 @@
 	 * @throws CoreException
 	 *             if an error occurs
 	 */
-	protected String createSuperTypeSource(final ICompilationUnit extractedWorkingCopy, final IType superType, final AbstractTypeDeclaration declaringDeclaration, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
+	protected String createSuperTypeSource(final ICompilationUnit extractedWorkingCopy, final IType superType, final AbstractTypeDeclaration declaringDeclaration, CompilationUnitRewrite declaringRewrite, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
 		Assert.isNotNull(extractedWorkingCopy);
 		Assert.isNotNull(declaringDeclaration);
 		Assert.isNotNull(status);
@@ -516,7 +749,7 @@
 			if (imports != null && !"".equals(imports)) { //$NON-NLS-1$
 				buffer.append(imports);
 			}
-			createTypeDeclaration(extractedWorkingCopy, superType, declaringDeclaration, typeComment, buffer, status, new SubProgressMonitor(monitor, 1));
+			createTypeDeclaration(extractedWorkingCopy, superType, declaringDeclaration, declaringRewrite, typeComment, buffer, status, new SubProgressMonitor(monitor, 1));
 			source= createTypeTemplate(extractedWorkingCopy, "", fileComment, "", buffer.toString()); //$NON-NLS-1$ //$NON-NLS-2$
 			if (source == null) {
 				if (!declaring.getPackageFragment().isDefaultPackage()) {
@@ -554,6 +787,8 @@
 	 *            <code>java.lang.Object</code>) is available
 	 * @param declaringDeclaration
 	 *            the declaration of the declaring type
+	 * @param declaringRewrite
+	 *            the CompilationUnitRewrite for the declaring type
 	 * @param comment
 	 *            the comment of the new type declaration
 	 * @param buffer
@@ -565,7 +800,7 @@
 	 * @throws CoreException
 	 *             if an error occurs
 	 */
-	protected void createTypeDeclaration(final ICompilationUnit extractedWorkingCopy, final IType superType, final AbstractTypeDeclaration declaringDeclaration, final String comment, final StringBuffer buffer, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
+	protected void createTypeDeclaration(final ICompilationUnit extractedWorkingCopy, final IType superType, final AbstractTypeDeclaration declaringDeclaration, CompilationUnitRewrite declaringRewrite, final String comment, final StringBuffer buffer, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
 		Assert.isNotNull(extractedWorkingCopy);
 		Assert.isNotNull(declaringDeclaration);
 		Assert.isNotNull(buffer);
@@ -607,7 +842,7 @@
 			final AbstractTypeDeclaration targetDeclaration= (AbstractTypeDeclaration) targetRewrite.getRoot().types().get(0);
 			createTypeParameters(targetRewrite, superType, declaringDeclaration, targetDeclaration);
 			createTypeSignature(targetRewrite, superType, declaringDeclaration, targetDeclaration);
-			createNecessaryConstructors(targetRewrite, superType, targetDeclaration, status);
+			createNecessaryConstructors(targetRewrite, superType, targetDeclaration, declaringDeclaration, declaringRewrite, status);
 			final TextEdit edit= targetRewrite.createChange(true).getEdit();
 			try {
 				edit.apply(document, TextEdit.UPDATE_REGIONS);
@@ -1103,6 +1338,7 @@
 					}
 				} else {
 					rewrite= fCompilationUnitRewrites.get(unit);
+					fixConstructorsWithFinalFieldInitialization(rewrite);
 					if (rewrite != null) {
 						final CompilationUnitChange layerChange= fLayerChanges.get(unit.getPrimary());
 						final CompilationUnitChange rewriteChange= rewrite.createChange(true);
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/PullUpRefactoringProcessor.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/PullUpRefactoringProcessor.java
index a5d2be9..a445e38 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/PullUpRefactoringProcessor.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/PullUpRefactoringProcessor.java
@@ -791,7 +791,7 @@
 		}
 	}
 
-	private RefactoringStatus checkFinalFields(final IProgressMonitor monitor) throws JavaModelException {
+	protected RefactoringStatus checkFinalFields(final IProgressMonitor monitor) throws JavaModelException {
 		final RefactoringStatus result= new RefactoringStatus();
 		monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, fMembersToMove.length);
 		for (final IMember member : fMembersToMove) {