Bug 570762 - Wrapping with try-with-resources should add catch statement

- add logic to QuickAssistProcessor.getTryWithResourceProposals
  to support selections within a try/catch block and to add
  a catch clause to handle any un-handled exceptions added by the
  auto-close
- do the same with AssignToVariableAssistProposal.doAddLocal() and
  SurroundWithTryResourcesRefactoring.createTryWithResourcesStatement()
- expose various methods in SurroundWithTryResourcesRefactoring
  for common usage
- add new methods to SurroundWithTryWithResourcesAnalyzer for
  common usage
- add new test scenarios to AssistQuickFixTest1d8 and
  SurroundWithResourcesTests1d8

Change-Id: Ic9cb39126ad9d299b8912ec7f8564432806df006
diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
index 45efba7..715d888 100644
--- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
+++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 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
@@ -30,6 +30,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -501,6 +502,26 @@
 		return ((ArrayType)type).getElementType();
 	}
 
+	/**
+	 * Filter a list of type bindings to remove any bindings that are sub-classes of others already in the list
+	 *
+	 * @param typeBindings - list of ITypeBinding to filter
+	 * @return updated list of ITypeBinding
+	 */
+	public static List<ITypeBinding> filterSubtypes(List<ITypeBinding> typeBindings) {
+		List<ITypeBinding> filteredBindings= new ArrayList<>(typeBindings);
+		for (Iterator<ITypeBinding> subtypeIterator= filteredBindings.iterator(); subtypeIterator.hasNext();) {
+			ITypeBinding iTypeBinding= subtypeIterator.next();
+			for (ITypeBinding superTypeBinding : filteredBindings) {
+				if (!iTypeBinding.equals(superTypeBinding) && iTypeBinding.isSubTypeCompatible(superTypeBinding)) {
+					subtypeIterator.remove();
+					break;
+				}
+			}
+		}
+		return filteredBindings;
+	}
+
 	public static ASTNode findDeclaration(IBinding binding, ASTNode root) {
 		root= root.getRoot();
 		if (root instanceof CompilationUnit) {
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_in/TestWithThrows2.java b/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_in/TestWithThrows2.java
new file mode 100644
index 0000000..f39f9f4
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_in/TestWithThrows2.java
@@ -0,0 +1,17 @@
+package trycatch18_in;
+
+import java.net.Socket;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.zip.DataFormatException;
+
+class TestWithThrows1 {
+	void foo(int a) throws FileNotFoundException {
+		/*[*/Socket s=new Socket();
+		FileInputStream is=new FileInputStream("a.b");
+		s.getInetAddress();/*]*/
+		if (s.getTcpNoDelay())
+			throw new DataFormatException();
+		is.available();
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_out/TestWithThrows2.java b/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_out/TestWithThrows2.java
new file mode 100644
index 0000000..74af139
--- /dev/null
+++ b/org.eclipse.jdt.ui.tests.refactoring/resources/SurroundWithWorkSpace/SurroundWithTests/tryresources18_out/TestWithThrows2.java
@@ -0,0 +1,22 @@
+package trycatch18_in;
+
+import java.net.Socket;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+
+class TestWithThrows1 {
+	void foo(int a) throws FileNotFoundException {
+		try (/*[*/ Socket s = new Socket();
+				FileInputStream is = new FileInputStream("a.b")) {
+			s.getInetAddress();/*]*/
+			if (s.getTcpNoDelay())
+				throw new DataFormatException();
+			is.available();
+		} catch (FileNotFoundException e) {
+			throw e;
+		} catch (DataFormatException | IOException e) {
+		}
+	}
+}
diff --git a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/SurroundWithResourcesTests1d8.java b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/SurroundWithResourcesTests1d8.java
index a898254..0956650 100644
--- a/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/SurroundWithResourcesTests1d8.java
+++ b/org.eclipse.jdt.ui.tests.refactoring/test cases/org/eclipse/jdt/ui/tests/refactoring/SurroundWithResourcesTests1d8.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2020 IBM Corporation and others.
+ * Copyright (c) 2020, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -115,6 +115,11 @@
 	}
 
 	@Test
+	public void testWithThrows2() throws Exception {
+		tryResourcesTest();
+	}
+
+	@Test
 	public void testInvalidStatement1() throws Exception {
 		tryResourcesInvalidTest();
 	}
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java
index e50a675..217150b 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java
@@ -5543,7 +5543,7 @@
 	}
 
 	@Test
-	public void testSurroundWithTryWithResource() throws Exception {
+	public void testSurroundWithTryWithResource_01() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("p", false, null);
 		StringBuffer bufOrg= new StringBuffer();
 		bufOrg.append("package p;\n");
@@ -5639,6 +5639,193 @@
 		assertCorrectLabels(proposals);
 	}
 	@Test
+	public void testSurroundWithTryWithResource_02() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("p", false, null);
+		StringBuffer bufOrg= new StringBuffer();
+		bufOrg.append("package p;\n");
+		bufOrg.append("\n");
+		bufOrg.append("import java.io.FileInputStream;\n");
+		bufOrg.append("import java.io.FileNotFoundException;\n");
+		bufOrg.append("import java.io.InputStream;\n");
+		bufOrg.append("import java.net.Socket;\n");
+		bufOrg.append("\n");
+		bufOrg.append("public class E {\n");
+		bufOrg.append("    public void foo() throws FileNotFoundException {\n");
+		bufOrg.append("        /*1*/Socket s = new Socket(), s2 = new Socket();\n");
+		bufOrg.append("        /*2*/InputStream is = s.getInputStream();\n");
+		bufOrg.append("        /*3*/FileInputStream f = new FileInputStream(\"a.b\");\n");
+		bufOrg.append("        /*4*/int i = 0;\n");
+		bufOrg.append("        System.out.println(s.getInetAddress().toString());\n");
+		bufOrg.append("        System.out.println(is.markSupported());/*0*/\n");
+		bufOrg.append("    }\n");
+		bufOrg.append("}\n");
+		bufOrg.append("\n");
+
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", bufOrg.toString(), false, null);
+		String strEnd= "/*0*/";
+
+		String str1= "/*1*/";
+		AssistContext context= getCorrectionContext(cu, bufOrg.toString().indexOf(str1), bufOrg.toString().indexOf(strEnd) - bufOrg.toString().indexOf(str1));
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		assertNumberOfProposals(proposals, 4);
+		assertCorrectLabels(proposals);
+
+		CUCorrectionProposal proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview1= getPreviewContent(proposal);
+
+		StringBuffer buf= new StringBuffer();
+		buf.append("package p;\n");
+		buf.append("\n");
+		buf.append("import java.io.FileInputStream;\n");
+		buf.append("import java.io.FileNotFoundException;\n");
+		buf.append("import java.io.IOException;\n");
+		buf.append("import java.io.InputStream;\n");
+		buf.append("import java.net.Socket;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo() throws FileNotFoundException {\n");
+		buf.append("        try (/*1*/Socket s = new Socket();\n");
+		buf.append("                Socket s2 = new Socket();\n");
+		buf.append("                /*2*/InputStream is = s.getInputStream();\n");
+		buf.append("                /*3*/FileInputStream f = new FileInputStream(\"a.b\")) {\n");
+		buf.append("            /*4*/int i = 0;\n");
+		buf.append("            System.out.println(s.getInetAddress().toString());\n");
+		buf.append("            System.out.println(is.markSupported());/*0*/\n");
+		buf.append("        } catch (FileNotFoundException e) {\n");
+		buf.append("            throw e;\n");
+		buf.append("        } catch (IOException e) {\n");
+		buf.append("            // TODO Auto-generated catch block\n");
+		buf.append("            e.printStackTrace();\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		buf.append("\n");
+		String expected1= buf.toString();
+
+		assertEqualStringsIgnoreOrder(new String[] { preview1 }, new String[] { expected1 });
+
+		str1= "/*2*/";
+		context= getCorrectionContext(cu, bufOrg.toString().indexOf(str1), bufOrg.toString().indexOf(strEnd) - bufOrg.toString().indexOf(str1));
+		proposals= collectAssists(context, false);
+
+		assertNumberOfProposals(proposals, 4);
+		assertCorrectLabels(proposals);
+
+		proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview2= getPreviewContent(proposal);
+
+		buf= new StringBuffer();
+		buf.append("package p;\n");
+		buf.append("\n");
+		buf.append("import java.io.FileInputStream;\n");
+		buf.append("import java.io.FileNotFoundException;\n");
+		buf.append("import java.io.IOException;\n");
+		buf.append("import java.io.InputStream;\n");
+		buf.append("import java.net.Socket;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo() throws FileNotFoundException {\n");
+		buf.append("        /*1*/Socket s = new Socket(), s2 = new Socket();\n");
+		buf.append("        try (/*2*/InputStream is = s.getInputStream();\n");
+		buf.append("                /*3*/FileInputStream f = new FileInputStream(\"a.b\")) {\n");
+		buf.append("            /*4*/int i = 0;\n");
+		buf.append("            System.out.println(s.getInetAddress().toString());\n");
+		buf.append("            System.out.println(is.markSupported());/*0*/\n");
+		buf.append("        } catch (FileNotFoundException e) {\n");
+		buf.append("            throw e;\n");
+		buf.append("        } catch (IOException e) {\n");
+		buf.append("            // TODO Auto-generated catch block\n");
+		buf.append("            e.printStackTrace();\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		buf.append("\n");
+		String expected2= buf.toString();
+
+		assertEqualStringsIgnoreOrder(new String[] { preview2 }, new String[] { expected2 });
+
+		str1= "/*4*/";
+		context= getCorrectionContext(cu, bufOrg.toString().indexOf(str1), bufOrg.toString().indexOf(strEnd) - bufOrg.toString().indexOf(str1));
+		proposals= collectAssists(context, false);
+
+		assertNumberOfProposals(proposals, 3);
+		assertCorrectLabels(proposals);
+	}
+	@Test
+	public void testSurroundWithTryWithResource_03() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("p", false, null);
+		StringBuffer bufOrg= new StringBuffer();
+		bufOrg.append("package p;\n");
+		bufOrg.append("\n");
+		bufOrg.append("import java.io.FileInputStream;\n");
+		bufOrg.append("import java.io.FileNotFoundException;\n");
+		bufOrg.append("import java.io.InputStream;\n");
+		bufOrg.append("import java.net.Socket;\n");
+		bufOrg.append("\n");
+		bufOrg.append("public class E {\n");
+		bufOrg.append("    public void foo() {\n");
+		bufOrg.append("        try {\n");
+		bufOrg.append("            /*1*/Socket s = new Socket(), s2 = new Socket();\n");
+		bufOrg.append("            /*2*/InputStream is = s.getInputStream();\n");
+		bufOrg.append("            /*3*/FileInputStream f = new FileInputStream(\"a.b\");\n");
+		bufOrg.append("            /*4*/int i = 0;\n");
+		bufOrg.append("            System.out.println(s.getInetAddress().toString());\n");
+		bufOrg.append("            System.out.println(is.markSupported());/*0*/\n");
+		bufOrg.append("        } catch (FileNotFoundException e) {\n");
+		bufOrg.append("        }\n");
+		bufOrg.append("    }\n");
+		bufOrg.append("}\n");
+		bufOrg.append("\n");
+
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", bufOrg.toString(), false, null);
+		String strEnd= "/*0*/";
+
+		String str1= "/*1*/";
+		AssistContext context= getCorrectionContext(cu, bufOrg.toString().indexOf(str1), bufOrg.toString().indexOf(strEnd) - bufOrg.toString().indexOf(str1));
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		assertCorrectLabels(proposals);
+
+		CUCorrectionProposal proposal= (CUCorrectionProposal) proposals.get(0);
+		String preview1= getPreviewContent(proposal);
+
+		StringBuffer buf= new StringBuffer();
+		buf.append("package p;\n");
+		buf.append("\n");
+		buf.append("import java.io.FileInputStream;\n");
+		buf.append("import java.io.FileNotFoundException;\n");
+		buf.append("import java.io.IOException;\n");
+		buf.append("import java.io.InputStream;\n");
+		buf.append("import java.net.Socket;\n");
+		buf.append("\n");
+		buf.append("public class E {\n");
+		buf.append("    public void foo() {\n");
+		buf.append("        try {\n");
+		buf.append("            try (/*1*/Socket s = new Socket();\n");
+		buf.append("                    Socket s2 = new Socket();\n");
+		buf.append("                    /*2*/InputStream is = s.getInputStream();\n");
+		buf.append("                    /*3*/FileInputStream f = new FileInputStream(\"a.b\")) {\n");
+		buf.append("                /*4*/int i = 0;\n");
+		buf.append("                System.out.println(s.getInetAddress().toString());\n");
+		buf.append("                System.out.println(is.markSupported());/*0*/\n");
+		buf.append("            } catch (FileNotFoundException e) {\n");
+		buf.append("                throw e;\n");
+		buf.append("            } catch (IOException e) {\n");
+		buf.append("                // TODO Auto-generated catch block\n");
+		buf.append("                e.printStackTrace();\n");
+		buf.append("            }\n");
+		buf.append("        } catch (FileNotFoundException e) {\n");
+		buf.append("        }\n");
+		buf.append("    }\n");
+		buf.append("}\n");
+		buf.append("\n");
+		String expected1= buf.toString();
+
+		assertEqualStringsIgnoreOrder(new String[] { preview1 }, new String[] { expected1 });
+		assertCorrectLabels(proposals);
+	}
+	@Test
 	public void testWrapInOptional_01() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("p", false, null);
 		StringBuffer buf= new StringBuffer();
@@ -5765,7 +5952,7 @@
 	}
 
 	@Test
-	public void testAssignInTryWithResources() throws Exception {
+	public void testAssignInTryWithResources_01() throws Exception {
 		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
 		String src=
 				"package test1;\n" +
@@ -5803,4 +5990,106 @@
 
 		assertExpectedExistInProposals(proposals, new String[] { expected });
 	}
+
+	@Test
+	public void testAssignInTryWithResources_02() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		String src=
+				"package test1;\n" +
+				"\n" +
+				"import java.io.FileInputStream;\n" +
+				"import java.io.FileNotFoundException;\n" +
+				"\n" +
+				"class E {\n" +
+				"    void f() throws FileNotFoundException {\n" +
+				"        new FileInputStream(\"f\");\n" +
+				"    }\n" +
+				"}";
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", src, false, null);
+
+		int offset= src.indexOf("new");
+		AssistContext context= getCorrectionContext(cu, offset, 0);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		assertNumberOfProposals(proposals, 6);
+		assertCorrectLabels(proposals);
+
+		String expected=
+				"package test1;\n" +
+				"\n" +
+				"import java.io.FileInputStream;\n" +
+				"import java.io.FileNotFoundException;\n" +
+				"import java.io.IOException;\n" +
+				"\n" +
+				"class E {\n" +
+				"    void f() throws FileNotFoundException {\n" +
+				"        try (FileInputStream fileInputStream = new FileInputStream(\"f\")) {\n" +
+				"            \n" +
+				"        } catch (FileNotFoundException e) {\n" +
+				"            throw e;\n" +
+				"        } catch (IOException e) {\n" +
+	            "            // TODO Auto-generated catch block\n" +
+				"            e.printStackTrace();\n" +
+				"        };\n" +
+				"    }\n" +
+				"}";
+
+		assertExpectedExistInProposals(proposals, new String[] { expected });
+	}
+
+	@Test
+	public void testAssignInTryWithResources_03() throws Exception {
+		IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null);
+		String src=
+				"package test1;\n" +
+				"\n" +
+				"import java.io.FileInputStream;\n" +
+				"import java.io.FileNotFoundException;\n" +
+				"\n" +
+				"class E {\n" +
+				"    void f() {\n" +
+				"        try {\n" +
+				"            new FileInputStream(\"f\");\n" +
+				"        } catch (FileNotFoundException e) {\n" +
+				"            // some action\n" +
+				"        }\n" +
+				"    }\n" +
+				"}";
+		ICompilationUnit cu= pack1.createCompilationUnit("E.java", src, false, null);
+
+		int offset= src.indexOf("new");
+		AssistContext context= getCorrectionContext(cu, offset, 0);
+		List<IJavaCompletionProposal> proposals= collectAssists(context, false);
+
+		assertNumberOfProposals(proposals, 6);
+		assertCorrectLabels(proposals);
+
+		String expected=
+				"package test1;\n" +
+				"\n" +
+				"import java.io.FileInputStream;\n" +
+				"import java.io.FileNotFoundException;\n" +
+				"import java.io.IOException;\n" +
+				"\n" +
+				"class E {\n" +
+				"    void f() {\n" +
+				"        try {\n" +
+				"            try (FileInputStream fileInputStream = new FileInputStream(\"f\")) {\n" +
+				"                \n" +
+				"            } catch (FileNotFoundException e) {\n" +
+				"                throw e;\n" +
+				"            } catch (IOException e) {\n" +
+	            "                // TODO Auto-generated catch block\n" +
+				"                e.printStackTrace();\n" +
+				"            };\n" +
+				"        } catch (FileNotFoundException e) {\n" +
+				"            // some action\n" +
+				"        }\n" +
+				"    }\n" +
+				"}";
+
+		assertExpectedExistInProposals(proposals, new String[] { expected });
+	}
+
 }
+
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesAnalyzer.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesAnalyzer.java
index d3c0872..bee6a31 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesAnalyzer.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesAnalyzer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2020 IBM Corporation and others.
+ * Copyright (c) 2020, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,6 +16,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -25,6 +26,7 @@
 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.CatchClause;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.IBinding;
 import org.eclipse.jdt.core.dom.IMethodBinding;
@@ -34,10 +36,14 @@
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.MethodReference;
 import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TryStatement;
 import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.UnionType;
 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
 
+import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
 import org.eclipse.jdt.internal.corext.dom.Bindings;
 import org.eclipse.jdt.internal.corext.dom.Selection;
 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
@@ -55,7 +61,7 @@
 
 	public ITypeBinding[] getExceptions(Selection selection) {
 		if (fEnclosingNode != null && !getStatus().hasFatalError()) {
-			fExceptions= ExceptionAnalyzer.perform(fEnclosingNode, selection, true);
+			fExceptions= ExceptionAnalyzer.perform(fEnclosingNode, selection, false);
 			if (fExceptions == null || fExceptions.length == 0) {
 				if (fEnclosingNode instanceof MethodReference) {
 					invalidSelection(RefactoringCoreMessages.SurroundWithTryCatchAnalyzer_doesNotContain);
@@ -72,6 +78,36 @@
 		return fExceptions;
 	}
 
+	public ITypeBinding[] getCaughtExceptions() {
+		List<ITypeBinding> exceptions= new ArrayList<>();
+		TryStatement enclosingTry= (TryStatement)ASTResolving.findAncestor(getFirstSelectedNode(), ASTNode.TRY_STATEMENT);
+		while (enclosingTry != null) {
+			List<CatchClause> catchClauses= enclosingTry.catchClauses();
+			for (CatchClause catchClause : catchClauses) {
+				SingleVariableDeclaration sdv= catchClause.getException();
+				Type type= sdv.getType();
+				if (type instanceof UnionType) {
+					UnionType unionType= (UnionType)type;
+					List<Type> types= unionType.types();
+					for (Type t : types) {
+						ITypeBinding binding= t.resolveBinding();
+						if (binding == null) {
+							break;
+						}
+						exceptions.add(binding);
+					}
+				} else {
+					ITypeBinding binding= type.resolveBinding();
+					if (binding != null) {
+						exceptions.add(binding);
+					}
+				}
+			}
+			enclosingTry= (TryStatement)ASTResolving.findAncestor(enclosingTry.getParent(), ASTNode.TRY_STATEMENT);
+		}
+		return exceptions.toArray(new ITypeBinding[0]);
+	}
+
 	public ITypeBinding[] getThrownExceptions() {
 		List<ITypeBinding> exceptions= new ArrayList<>();
 		if (fEnclosingNode.getNodeType() == ASTNode.METHOD_DECLARATION) {
@@ -99,6 +135,49 @@
 		return exceptions.toArray(new ITypeBinding[0]);
 	}
 
+	public List<ITypeBinding> calculateCatchesAndRethrows(List<ITypeBinding> exceptions, List<ITypeBinding> mustRethrowList) {
+		List<ITypeBinding> exceptionList= new ArrayList<>(exceptions);
+		ITypeBinding[] caughtExceptions= getCaughtExceptions();
+		if (caughtExceptions.length > 0) {
+			for (Iterator<ITypeBinding> iter= exceptionList.iterator(); iter.hasNext();) {
+				ITypeBinding binding= iter.next();
+				for (ITypeBinding caughtException : caughtExceptions) {
+					if (binding.isAssignmentCompatible(caughtException)) {
+						iter.remove();
+						break;
+					}
+				}
+			}
+			for (ITypeBinding binding : exceptionList) {
+				for (ITypeBinding caughtException : caughtExceptions) {
+					if (caughtException.isAssignmentCompatible(binding)) {
+						mustRethrowList.add(caughtException);
+						break;
+					}
+				}
+			}
+		}
+		ITypeBinding[] thrownExceptions= getThrownExceptions();
+		for (Iterator<ITypeBinding> iter= exceptionList.iterator(); iter.hasNext();) {
+			ITypeBinding binding= iter.next();
+			for (ITypeBinding thrownException : thrownExceptions) {
+				if (binding.isAssignmentCompatible(thrownException)) {
+					iter.remove();
+					break;
+				}
+			}
+		}
+		for (ITypeBinding binding : exceptionList) {
+			for (ITypeBinding thrownException : thrownExceptions) {
+				if (thrownException.isAssignmentCompatible(binding)) {
+					mustRethrowList.add(thrownException);
+					break;
+				}
+			}
+		}
+		return exceptionList;
+	}
+
 	public ASTNode getEnclosingNode() {
 		return fEnclosingNode;
 	}
diff --git a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesRefactoring.java b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesRefactoring.java
index 8c4f6bd..645126c 100644
--- a/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesRefactoring.java
+++ b/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/surround/SurroundWithTryWithResourcesRefactoring.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2020 IBM Corporation and others.
+ * Copyright (c) 2020, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -58,6 +58,7 @@
 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.ThrowStatement;
 import org.eclipse.jdt.core.dom.TryStatement;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.UnionType;
@@ -410,19 +411,9 @@
 				if (typeBinding != null) {
 					IMethodBinding close= findAutocloseMethod(typeBinding);
 					if (close != null) {
-						ITypeBinding[] thrownExceptions= fAnalyzer.getThrownExceptions();
 						for (ITypeBinding exceptionType : close.getExceptionTypes()) {
 							if (!allExceptions.contains(exceptionType)) {
-								boolean isThrown= false;
-								for (ITypeBinding thrownException : thrownExceptions) {
-									if (exceptionType.isAssignmentCompatible(thrownException)) {
-										isThrown= true;
-										break;
-									}
-								}
-								if (!isThrown) {
-									allExceptions.add(exceptionType);
-								}
+								allExceptions.add(exceptionType);
 							}
 						}
 					}
@@ -470,11 +461,28 @@
 			}
 		}
 
-		List<ITypeBinding> filteredExceptions= filterSubtypeExceptions(allExceptions);
-		if (allExceptions.size() > 0) {
+		List<ITypeBinding> mustRethrowList= new ArrayList<>();
+		List<ITypeBinding> catchExceptions= fAnalyzer.calculateCatchesAndRethrows(ASTNodes.filterSubtypes(allExceptions), mustRethrowList);
+		List<ITypeBinding> filteredExceptions= ASTNodes.filterSubtypes(catchExceptions);
+		if (catchExceptions.size() > 0) {
+			LinkedProposalModel linkedProposalModel= new LinkedProposalModel();
+			int i= 0;
+			for (ITypeBinding mustThrow : mustRethrowList) {
+				CatchClause newClause= ast.newCatchClause();
+				SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration();
+				newDecl.setName(ast.newSimpleName(name));
+				Type importType= fImportRewrite.addImport(mustThrow, ast, context, TypeLocation.EXCEPTION);
+				newDecl.setType(importType);
+				newClause.setException(newDecl);
+				ThrowStatement newThrowStatement= ast.newThrowStatement();
+				newThrowStatement.setExpression(ast.newSimpleName(name));
+				linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition(fRewriter.track(decl.getName()), false);
+				newClause.getBody().statements().add(newThrowStatement);
+				tryStatement.catchClauses().add(newClause);
+				++i;
+			}
 			UnionType unionType= getAST().newUnionType();
 			List<Type> types= unionType.types();
-			int i=0;
 			for (ITypeBinding exception : filteredExceptions) {
 				Type type= fImportRewrite.addImport(exception, getAST(), context, TypeLocation.EXCEPTION);
 				types.add(type);
@@ -549,7 +557,7 @@
 
 	}
 
-	private IMethodBinding findAutocloseMethod(ITypeBinding type) {
+	public static IMethodBinding findAutocloseMethod(ITypeBinding type) {
 		while (type != null) {
 			IMethodBinding[] methods= type.getDeclaredMethods();
 			for (IMethodBinding method : methods) {
@@ -581,7 +589,7 @@
 	}
 
 	// find all nodes (statements) that are within the start/end positions
-	private List<ASTNode> findNodesInRange(ASTNode astNode, final int start, final int end) {
+	public static List<ASTNode> findNodesInRange(ASTNode astNode, final int start, final int end) {
 		List<ASTNode> nodesInRange= new ArrayList<>();
 		astNode.accept(new ASTVisitor() {
 			int pre= start;
@@ -602,7 +610,8 @@
 							Statement pStatement= (Statement) statement.getParent();
 							switch (pStatement.getNodeType()) {
 								case ASTNode.BLOCK:
-									if (pStatement.getParent().getNodeType() != ASTNode.METHOD_DECLARATION) {
+									if (pStatement.getParent().getNodeType() != ASTNode.METHOD_DECLARATION &&
+									pStatement.getParent().getNodeType() != ASTNode.TRY_STATEMENT) {
 										statement= pStatement;
 										continue;
 									} else {
@@ -628,20 +637,6 @@
 		return nodesInRange;
 	}
 
-	private List<ITypeBinding> filterSubtypeExceptions(List<ITypeBinding> exceptions) {
-		List<ITypeBinding> filteredExceptions= new ArrayList<>(exceptions);
-		for (Iterator<ITypeBinding> subtypeIterator= filteredExceptions.iterator(); subtypeIterator.hasNext();) {
-			ITypeBinding iTypeBinding= subtypeIterator.next();
-			for (ITypeBinding superTypeBinding : filteredExceptions) {
-				if (!iTypeBinding.equals(superTypeBinding) && iTypeBinding.isSubTypeCompatible(superTypeBinding)) {
-					subtypeIterator.remove();
-					break;
-				}
-			}
-		}
-		return filteredExceptions;
-	}
-
 	private List<ASTNode> getSpecialVariableDeclarationStatements() {
 		List<ASTNode> result= new ArrayList<>(3);
 		for (VariableDeclaration local : fAnalyzer.getAffectedLocals()) {
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
index 15201aa..9a06d22 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickAssistProcessor.java
@@ -126,6 +126,7 @@
 import org.eclipse.jdt.core.dom.SwitchStatement;
 import org.eclipse.jdt.core.dom.SynchronizedStatement;
 import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.ThrowStatement;
 import org.eclipse.jdt.core.dom.TryStatement;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
@@ -152,6 +153,7 @@
 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
 import org.eclipse.jdt.internal.corext.dom.DimensionRewrite;
 import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher;
 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
@@ -179,6 +181,8 @@
 import org.eclipse.jdt.internal.corext.refactoring.code.Invocations;
 import org.eclipse.jdt.internal.corext.refactoring.code.PromoteTempToFieldRefactoring;
 import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover;
+import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesRefactoring;
 import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
@@ -3307,7 +3311,7 @@
 	}
 
 	private static boolean getTryWithResourceProposals(IInvocationContext context, ASTNode node, ArrayList<ASTNode> coveredNodes, Collection<ICommandAccess> resultingCollections)
-			throws IllegalArgumentException, JavaModelException {
+			throws IllegalArgumentException, CoreException {
 
 		ASTNode parentStatement= ASTResolving.findAncestor(node, ASTNode.VARIABLE_DECLARATION_STATEMENT);
 		if (!(parentStatement instanceof VariableDeclarationStatement) &&
@@ -3334,9 +3338,7 @@
 		if (coveredAutoClosableNodes.isEmpty()) {
 			return false;
 		}
-		if (isParentTryStatement(coveredAutoClosableNodes.get(0))) {
-			return false;
-		}
+
 		BodyDeclaration parentBodyDeclaration= ASTResolving.findParentBodyDeclaration(node);
 		int start= coveredAutoClosableNodes.get(0).getStartPosition();
 		int end= start;
@@ -3347,7 +3349,7 @@
 		}
 
 		// recursive loop to find all nodes affected by wrapping in try block
-		List<ASTNode> nodesInRange= findNodesInRange(parentBodyDeclaration, start, end);
+		List<ASTNode> nodesInRange= SurroundWithTryWithResourcesRefactoring.findNodesInRange(parentBodyDeclaration, start, end);
 		int oldEnd= end;
 		while (true) {
 			int newEnd= oldEnd;
@@ -3357,7 +3359,7 @@
 			}
 			if (newEnd > oldEnd) {
 				oldEnd= newEnd;
-				nodesInRange= findNodesInRange(parentBodyDeclaration, start, newEnd);
+				nodesInRange= SurroundWithTryWithResourcesRefactoring.findNodesInRange(parentBodyDeclaration, start, newEnd);
 				continue;
 			}
 			break;
@@ -3371,6 +3373,14 @@
 		TryStatement newTryStatement= ast.newTryStatement();
 		Block newTryBody= ast.newBlock();
 		newTryStatement.setBody(newTryBody);
+		ICompilationUnit icu= context.getCompilationUnit();
+		ASTNode lastNode= nodesInRange.isEmpty() ? coveredAutoClosableNodes.get(coveredAutoClosableNodes.size() - 1)
+				: nodesInRange.get(nodesInRange.size() - 1);
+		Selection selection= Selection.createFromStartLength(start, lastNode.getStartPosition() - start + lastNode.getLength());
+		SurroundWithTryWithResourcesAnalyzer analyzer= new SurroundWithTryWithResourcesAnalyzer(icu, selection);
+		cu.accept(analyzer);
+		ITypeBinding[] exceptions= analyzer.getExceptions(analyzer.getSelection());
+		List<ITypeBinding> allExceptions= new ArrayList<>(Arrays.asList(exceptions));
 		for (ASTNode coveredNode : coveredAutoClosableNodes) {
 			ASTNode findAncestor= ASTResolving.findAncestor(coveredNode, ASTNode.VARIABLE_DECLARATION_STATEMENT);
 			if (findAncestor == null) {
@@ -3384,6 +3394,17 @@
 					commentToken= buffer.getText(extendedStatementStart, vds.getStartPosition() - extendedStatementStart);
 				}
 				Type type= vds.getType();
+				ITypeBinding typeBinding= type.resolveBinding();
+				if (typeBinding != null) {
+					IMethodBinding close= SurroundWithTryWithResourcesRefactoring.findAutocloseMethod(typeBinding);
+					if (close != null) {
+						for (ITypeBinding exceptionType : close.getExceptionTypes()) {
+							if (!allExceptions.contains(exceptionType)) {
+								allExceptions.add(exceptionType);
+							}
+						}
+					}
+				}
 				String typeName= buffer.getText(type.getStartPosition(), type.getLength());
 
 				for (Object object : vds.fragments()) {
@@ -3420,10 +3441,74 @@
 			return false;
 		}
 
+		String label= CorrectionMessages.QuickAssistProcessor_convert_to_try_with_resource;
+		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
+		LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.SURROUND_WITH_TRY_CATCH, image);
+
+		ImportRewrite imports= proposal.createImportRewrite(context.getASTRoot());
+		ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(node, imports);
+
+		CatchClause catchClause= ast.newCatchClause();
+		SingleVariableDeclaration decl= ast.newSingleVariableDeclaration();
+		String varName= StubUtility.getExceptionVariableName(icu.getJavaProject());
+		parentBodyDeclaration.getRoot().accept(analyzer);
+		CodeScopeBuilder.Scope scope= CodeScopeBuilder.perform(analyzer.getEnclosingBodyDeclaration(), selection).
+				findScope(selection.getOffset(), selection.getLength());
+		scope.setCursor(selection.getOffset());
+		String name= scope.createName(varName, false);
+		decl.setName(ast.newSimpleName(name));
+
+		List<ITypeBinding> mustRethrowList= new ArrayList<>();
+		List<ITypeBinding> catchExceptions= analyzer.calculateCatchesAndRethrows(ASTNodes.filterSubtypes(allExceptions), mustRethrowList);
+		List<ITypeBinding> filteredExceptions= ASTNodes.filterSubtypes(catchExceptions);
+
+		if (catchExceptions.size() > 0) {
+			final String GROUP_EXC_NAME= "exc_name"; //$NON-NLS-1$
+			final String GROUP_EXC_TYPE= "exc_type"; //$NON-NLS-1$
+			LinkedProposalModel linkedProposalModel= new LinkedProposalModel();
+
+			int i= 0;
+			for (ITypeBinding mustThrow : mustRethrowList) {
+				CatchClause newClause= ast.newCatchClause();
+				SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration();
+				newDecl.setName(ast.newSimpleName(name));
+				Type importType= imports.addImport(mustThrow, ast, importRewriteContext, TypeLocation.EXCEPTION);
+				newDecl.setType(importType);
+				newClause.setException(newDecl);
+				ThrowStatement newThrowStatement= ast.newThrowStatement();
+				newThrowStatement.setExpression(ast.newSimpleName(name));
+				linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition(rewrite.track(decl.getName()), false);
+				newClause.getBody().statements().add(newThrowStatement);
+				newTryStatement.catchClauses().add(newClause);
+				++i;
+			}
+			UnionType unionType= ast.newUnionType();
+			List<Type> types= unionType.types();
+			for (ITypeBinding exception : filteredExceptions) {
+				Type type= imports.addImport(exception, ast, importRewriteContext, TypeLocation.EXCEPTION);
+				types.add(type);
+				linkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition(rewrite.track(type), i == 0);
+				i++;
+			}
+
+			decl.setType(unionType);
+			catchClause.setException(decl);
+			linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + 0, true).addPosition(rewrite.track(decl.getName()), false);
+			Statement st= null;
+			String s= StubUtility.getCatchBodyContent(icu, "Exception", name, coveredNodes.get(0), icu.findRecommendedLineSeparator()); //$NON-NLS-1$
+			if (s != null) {
+				st= (Statement)rewrite.createStringPlaceholder(s, ASTNode.RETURN_STATEMENT);
+			}
+			if (st != null) {
+				catchClause.getBody().statements().add(st);
+			}
+			newTryStatement.catchClauses().add(catchClause);
+		}
+
 		if (!nodesInRange.isEmpty()) {
 			ASTNode firstNode= nodesInRange.get(0);
-			MethodDeclaration methodDeclaration= ASTResolving.findParentMethodDeclaration(firstNode);
-			ListRewrite listRewrite= rewrite.getListRewrite(methodDeclaration.getBody(), Block.STATEMENTS_PROPERTY);
+			ASTNode methodDeclaration= ASTResolving.findAncestor(firstNode, ASTNode.BLOCK);
+			ListRewrite listRewrite= rewrite.getListRewrite(methodDeclaration, Block.STATEMENTS_PROPERTY);
 			ASTNode createCopyTarget= listRewrite.createMoveTarget(firstNode, nodesInRange.get(nodesInRange.size() - 1));
 			rewrite.getListRewrite(newTryBody, Block.STATEMENTS_PROPERTY).insertFirst(createCopyTarget, null);
 		}
@@ -3434,24 +3519,10 @@
 			rewrite.remove(coveredAutoClosableNodes.get(i), null);
 		}
 
-		String label= CorrectionMessages.QuickAssistProcessor_convert_to_try_with_resource;
-		Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
-		ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(
-				label, context.getCompilationUnit(), rewrite, IProposalRelevance.SURROUND_WITH_TRY_CATCH, image);
 		resultingCollections.add(proposal);
 		return true;
 	}
 
-	private static boolean isParentTryStatement(ASTNode astNode) {
-		while (astNode != null) {
-			if (astNode instanceof TryStatement) {
-				return true;
-			}
-			astNode= astNode.getParent();
-		}
-		return false;
-	}
-
 	private static int findEndPostion(ASTNode node) {
 		int end= node.getStartPosition() + node.getLength();
 		Map<SimpleName, IVariableBinding> nodeSimpleNameBindings= getVariableStatementBinding(node);
@@ -3535,54 +3606,6 @@
 		return variableBindings;
 	}
 
-	// find all nodes (statements) that are within the start/end positions
-	private static List<ASTNode> findNodesInRange(ASTNode astNode, final int start, final int end) {
-		List<ASTNode> nodesInRange= new ArrayList<>();
-		astNode.accept(new ASTVisitor() {
-			int pre= start;
-
-			@Override
-			public void preVisit(ASTNode preNode) {
-				pre= preNode.getStartPosition();
-				super.preVisit(preNode);
-			}
-
-			@Override
-			public void postVisit(ASTNode postNode) {
-				int post= postNode.getStartPosition() + postNode.getLength();
-				if (pre >= start && post <= end) {
-					Statement statement= ASTResolving.findParentStatement(postNode);
-					loop: while (statement != null) {
-						if (statement.getParent() instanceof Statement) {
-							Statement pStatement= (Statement) statement.getParent();
-							switch (pStatement.getNodeType()) {
-								case ASTNode.BLOCK:
-									if (pStatement.getParent().getNodeType() != ASTNode.METHOD_DECLARATION) {
-										statement= pStatement;
-										continue;
-									} else {
-										break loop;
-									}
-								case ASTNode.METHOD_DECLARATION:
-									break loop;
-								default:
-									break;
-							}
-							statement= pStatement;
-						} else {
-							break;
-						}
-					}
-					if (statement != null && !nodesInRange.contains(statement)) {
-						nodesInRange.add(statement);
-					}
-				}
-				super.postVisit(postNode);
-			}
-		});
-		return nodesInRange;
-	}
-
 	private static boolean getAddBlockProposals(IInvocationContext context, ASTNode node, Collection<ICommandAccess> resultingCollections) {
 		if (!(node instanceof Statement)) {
 			return false;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java
index 00c2f82..a8a3ce3 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java
@@ -37,6 +37,7 @@
 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.CatchClause;
 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.EmptyStatement;
@@ -44,6 +45,7 @@
 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.IMethodBinding;
 import org.eclipse.jdt.core.dom.ITypeBinding;
 import org.eclipse.jdt.core.dom.IVariableBinding;
 import org.eclipse.jdt.core.dom.Initializer;
@@ -52,8 +54,10 @@
 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.ThrowStatement;
 import org.eclipse.jdt.core.dom.TryStatement;
 import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.UnionType;
 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
@@ -69,11 +73,17 @@
 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
+import org.eclipse.jdt.internal.corext.dom.Selection;
 import org.eclipse.jdt.internal.corext.dom.TokenScanner;
 import org.eclipse.jdt.internal.corext.fix.CleanUpConstants;
 import org.eclipse.jdt.internal.corext.fix.CleanUpPostSaveListener;
 import org.eclipse.jdt.internal.corext.fix.CleanUpPreferenceUtil;
+import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
+import org.eclipse.jdt.internal.corext.refactoring.surround.ExceptionAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesAnalyzer;
+import org.eclipse.jdt.internal.corext.refactoring.surround.SurroundWithTryWithResourcesRefactoring;
 import org.eclipse.jdt.internal.corext.util.Messages;
 
 import org.eclipse.jdt.ui.PreferenceConstants;
@@ -98,16 +108,20 @@
 
 	private final String KEY_NAME= "name";  //$NON-NLS-1$
 	private final String KEY_TYPE= "type";  //$NON-NLS-1$
+	private final String GROUP_EXC_TYPE= "exc_type"; //$NON-NLS-1$
+	private final String GROUP_EXC_NAME= "exc_name"; //$NON-NLS-1$
 
 	private final int  fVariableKind;
 	private final List<ASTNode> fNodesToAssign; // ExpressionStatement or SingleVariableDeclaration(s)
 	private final ITypeBinding fTypeBinding;
+	private final ICompilationUnit fCUnit;
 
 	private VariableDeclarationFragment fExistingFragment;
 
 	public AssignToVariableAssistProposal(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance) {
 		super("", cu, null, relevance, null); //$NON-NLS-1$
 
+		fCUnit= cu;
 		fVariableKind= variableKind;
 		fNodesToAssign= new ArrayList<>();
 		fNodesToAssign.add(node);
@@ -129,6 +143,7 @@
 	public AssignToVariableAssistProposal(ICompilationUnit cu, SingleVariableDeclaration parameter, VariableDeclarationFragment existingFragment, ITypeBinding typeBinding, int relevance) {
 		super("", cu, null, relevance, null); //$NON-NLS-1$
 
+		fCUnit= cu;
 		fVariableKind= FIELD;
 		fNodesToAssign= new ArrayList<>();
 		fNodesToAssign.add(parameter);
@@ -146,6 +161,7 @@
 	public AssignToVariableAssistProposal(ICompilationUnit cu, List<SingleVariableDeclaration> parameters, int relevance) {
 		super("", cu, null, relevance, null); //$NON-NLS-1$
 
+		fCUnit= cu;
 		fVariableKind= FIELD;
 		fNodesToAssign= new ArrayList<>();
 		fNodesToAssign.addAll(parameters);
@@ -169,14 +185,14 @@
 		}
 	}
 
-	private ASTRewrite doAddLocal() {
+	private ASTRewrite doAddLocal() throws CoreException {
 		ASTNode nodeToAssign= fNodesToAssign.get(0);
 		Expression expression= ((ExpressionStatement) nodeToAssign).getExpression();
 		AST ast= nodeToAssign.getAST();
 
 		ASTRewrite rewrite= ASTRewrite.create(ast);
 
-		createImportRewrite((CompilationUnit) nodeToAssign.getRoot());
+		ImportRewrite importRewrite= createImportRewrite((CompilationUnit) nodeToAssign.getRoot());
 
 		String[] varNames= suggestLocalVariableNames(fTypeBinding, expression);
 		for (String varName : varNames) {
@@ -217,6 +233,69 @@
 			EmptyStatement blankLine = (EmptyStatement) rewrite.createStringPlaceholder("", ASTNode.EMPTY_STATEMENT); //$NON-NLS-1$
 			tryStatement.getBody().statements().add(blankLine);
 
+			CatchClause catchClause= ast.newCatchClause();
+			SingleVariableDeclaration decl= ast.newSingleVariableDeclaration();
+			Selection selection= Selection.createFromStartLength(expression.getStartPosition(), expression.getLength());
+			String varName= StubUtility.getExceptionVariableName(fCUnit.getJavaProject());
+			SurroundWithTryWithResourcesAnalyzer analyzer= new SurroundWithTryWithResourcesAnalyzer(fCUnit, selection);
+			expression.getRoot().accept(analyzer);
+			CodeScopeBuilder.Scope scope= CodeScopeBuilder.perform(analyzer.getEnclosingBodyDeclaration(), selection).
+					findScope(selection.getOffset(), selection.getLength());
+			scope.setCursor(selection.getOffset());
+			String name= scope.createName(varName, false);
+			decl.setName(ast.newSimpleName(name));
+			ITypeBinding[] exceptions= 	ExceptionAnalyzer.perform(expression.getParent(), selection, false);
+			List<ITypeBinding> allExceptions= new ArrayList<>(Arrays.asList(exceptions));
+			List<ITypeBinding> mustRethrowList= new ArrayList<>();
+
+			if (fTypeBinding != null) {
+				IMethodBinding close= SurroundWithTryWithResourcesRefactoring.findAutocloseMethod(fTypeBinding);
+				if (close != null) {
+					for (ITypeBinding exceptionType : close.getExceptionTypes()) {
+						if (!allExceptions.contains(exceptionType)) {
+							allExceptions.add(exceptionType);
+						}
+					}
+				}
+			}
+			List<ITypeBinding> catchExceptions= analyzer.calculateCatchesAndRethrows(ASTNodes.filterSubtypes(allExceptions), mustRethrowList);
+			if (catchExceptions.size() > 0) {
+				ImportRewriteContext context= new ContextSensitiveImportRewriteContext(analyzer.getEnclosingBodyDeclaration(), importRewrite);
+				LinkedProposalModel linkedProposalModel= new LinkedProposalModel();
+				int i= 0;
+				for (ITypeBinding mustThrow : mustRethrowList) {
+					CatchClause newClause= ast.newCatchClause();
+					SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration();
+					newDecl.setName(ast.newSimpleName(name));
+					Type importType= importRewrite.addImport(mustThrow, ast, context, TypeLocation.EXCEPTION);
+					newDecl.setType(importType);
+					newClause.setException(newDecl);
+					ThrowStatement newThrowStatement= ast.newThrowStatement();
+					newThrowStatement.setExpression(ast.newSimpleName(name));
+					linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition(rewrite.track(decl.getName()), false);
+					newClause.getBody().statements().add(newThrowStatement);
+					tryStatement.catchClauses().add(newClause);
+					++i;
+				}
+				List<ITypeBinding> filteredExceptions= ASTNodes.filterSubtypes(catchExceptions);
+				UnionType unionType= ast.newUnionType();
+				List<Type> types= unionType.types();
+				for (ITypeBinding exception : filteredExceptions) {
+					Type importType= importRewrite.addImport(exception, ast, context, TypeLocation.EXCEPTION);
+					types.add(importType);
+					linkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition(rewrite.track(type), i == 0);
+					i++;
+				}
+				decl.setType(unionType);
+				catchClause.setException(decl);
+				linkedProposalModel.getPositionGroup(GROUP_EXC_NAME + 0, true).addPosition(rewrite.track(decl.getName()), false);
+				Statement st= getCatchBody(rewrite, expression, "Exception", name, fCUnit.findRecommendedLineSeparator()); //$NON-NLS-1$
+				if (st != null) {
+					catchClause.getBody().statements().add(st);
+				}
+				tryStatement.catchClauses().add(catchClause);
+			}
+
 			rewrite.replace(expression, tryStatement, null);
 			setEndPosition(rewrite.track(blankLine));
 		}
@@ -227,6 +306,15 @@
 		return rewrite;
 	}
 
+	private Statement getCatchBody(ASTRewrite rewrite, Expression expression, String type, String name, String lineSeparator) throws CoreException {
+		String s= StubUtility.getCatchBodyContent(fCUnit, type, name, expression, lineSeparator);
+		if (s == null) {
+			return null;
+		} else {
+			return (Statement)rewrite.createStringPlaceholder(s, ASTNode.RETURN_STATEMENT);
+		}
+	}
+
 	private boolean needsSemicolon(Expression expression) {
 		if ((expression.getParent().getFlags() & ASTNode.RECOVERED) != 0) {
 			try {