Merge branch 'master' into BETA_JAVA16 plus fixing minor version error

Signed-off-by: Manoj Palat <manpalat@in.ibm.com>
diff --git a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
index df66395..c8aae87 100644
--- a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.core.tests.builder; singleton:=true
-Bundle-Version: 3.10.1000.qualifier
+Bundle-Version: 3.10.1100.qualifier
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Export-Package: org.eclipse.jdt.core.tests.builder
diff --git a/org.eclipse.jdt.core.tests.builder/pom.xml b/org.eclipse.jdt.core.tests.builder/pom.xml
index c2afdbf..26f479c 100644
--- a/org.eclipse.jdt.core.tests.builder/pom.xml
+++ b/org.eclipse.jdt.core.tests.builder/pom.xml
@@ -19,7 +19,7 @@
   </parent>
   <groupId>org.eclipse.jdt</groupId>
   <artifactId>org.eclipse.jdt.core.tests.builder</artifactId>
-  <version>3.10.1000-SNAPSHOT</version>
+  <version>3.10.1100-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
 
   <properties>
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
index fed6380..3a54ae9 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/StateTest.java
@@ -28,11 +28,16 @@
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IAccessRule;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.tests.util.Util;
 import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
+import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
 import org.eclipse.jdt.internal.core.builder.JavaBuilder;
 import org.eclipse.jdt.internal.core.builder.ReferenceCollection;
 import org.eclipse.jdt.internal.core.builder.State;
@@ -108,6 +113,56 @@
 		writeReadAndCompareReferences(project);
 	}
 
+	public void testBug567532() throws JavaModelException, Exception {
+		IPath project = env.addProject("Bug567532"); //$NON-NLS-1$
+		String[] classLibs = Util.getJavaClassLibs();
+		for (String jar : classLibs) {
+			env.addEntry(project,
+					JavaCore.newLibraryEntry(
+							new Path(jar),
+							null,
+							null,
+							new IAccessRule[0],
+							new IClasspathAttribute[] {
+									JavaCore.newClasspathAttribute(IClasspathAttribute.TEST, "true"),
+									JavaCore.newClasspathAttribute(IClasspathAttribute.ADD_EXPORTS,
+											"jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED") },
+							false));
+		}
+
+		env.addClass(project, "a", "WithOther", //$NON-NLS-1$ //$NON-NLS-2$
+			"package a;\n" +
+			"class Other {\n" +
+			"}\n" +
+			"public class WithOther {\n" +
+			"}" //$NON-NLS-1$
+		);
+		fullBuild();
+		env.removePackage(project, "a");
+		incrementalBuild();
+
+		writeReadAndCompareTestBinaryLocations(project);
+	}
+
+	private void writeReadAndCompareTestBinaryLocations(IPath projectPath)
+			throws JavaModelException, IOException, CoreException {
+		JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
+		IProject project = env.getProject(projectPath);
+		PerProjectInfo info = javaModelManager.getPerProjectInfoCheckExistence(project);
+		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+		State savedState = (State) info.savedState;
+		JavaBuilder.writeState(savedState, new DataOutputStream(outputStream));
+		byte[] bytes = outputStream.toByteArray();
+		State readState = JavaBuilder.readState(project, new DataInputStream(new ByteArrayInputStream(bytes)));
+		assertEqualBinaryLocations(savedState.testBinaryLocations, readState.testBinaryLocations);
+	}
+
+	private void assertEqualBinaryLocations(ClasspathLocation[] a,
+			ClasspathLocation[] b) {
+		assertEquals(a.length, b.length);
+		assertArrayEquals(a, b);
+	}
+
 	private void writeReadAndCompareReferences(IPath projectPath)
 			throws JavaModelException, IOException, CoreException {
 		JavaModelManager javaModelManager = JavaModelManager.getJavaModelManager();
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ComplianceDiagnoseTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ComplianceDiagnoseTest.java
index 41978bd..2d77b89 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ComplianceDiagnoseTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ComplianceDiagnoseTest.java
@@ -3232,17 +3232,18 @@
 		"}\n",
 	};
 	String usLevel = this.complianceLevel < ClassFileConstants.JDK9 ? "WARNING" : "ERROR";
+	String errorMessage = this.complianceLevel < ClassFileConstants.JDK9 ? "\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" : "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n";
 	String expectedProblemLog =
 			"----------\n" +
 			"1. " + usLevel +" in X.java (at line 2)\n" +
 			"	int _;\n" +
 			"	    ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			errorMessage +
 			"----------\n" +
 			"2. " + usLevel +" in X.java (at line 4)\n" +
 			"	int _   = 3;\n" +
 			"	    ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			errorMessage +
 			"----------\n" +
 			"3. WARNING in X.java (at line 4)\n" +
 			"	int _   = 3;\n" +
@@ -3252,7 +3253,7 @@
 			"4. " + usLevel +" in X.java (at line 8)\n" +
 			"	void goo(int _) {}\n" +
 			"	             ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			errorMessage +
 			"----------\n" +
 			"5. WARNING in X.java (at line 8)\n" +
 			"	void goo(int _) {}\n" +
@@ -3262,7 +3263,7 @@
 			"6. " + usLevel +" in X.java (at line 11)\n" +
 			"	} catch (Exception _) {\n" +
 			"	                   ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			errorMessage +
 			"----------\n" +
 			"7. WARNING in X.java (at line 11)\n" +
 			"	} catch (Exception _) {\n" +
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest2.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest2.java
index 1f5c855..cf0fa27 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest2.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest2.java
@@ -14,7 +14,10 @@
 package org.eclipse.jdt.core.tests.compiler.regression;
 
 import java.io.File;
+import java.io.IOException;
 
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.tests.util.Util;
 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 
 import junit.framework.Test;
@@ -379,4 +382,41 @@
 					"",
 					true);
 }
+public void testBug568802() {
+	String currentWorkingDirectoryPath = System.getProperty("user.dir");
+	String libPath = currentWorkingDirectoryPath + File.separator + "lib568802.jar";
+	try {
+	Util.createJar(
+		new String[] {
+			"hello/World.java;\n",
+			"package hello;\n"
+					+    "public class World {}\n",
+			"module-info.java;\n",
+			"module HelloModule {}\n"
+		},
+		libPath,
+		JavaCore.VERSION_11,
+		false);
+	this.runConformTest(
+			new String[] {
+					"X.java",
+					"import hello.World;\n"
+					+ "public class X {\n"
+					+ "	 World field = new World();\n"
+					+ "}\n"
+			},
+			"\"" + OUTPUT_DIR +  File.separator + "X.java\"" +
+					" -cp " + libPath + // relative
+					" -source " + CompilerOptions.getLatestVersion() +
+					" -target " + CompilerOptions.getLatestVersion() + " ",
+					"",
+					"",
+					true);
+	} catch (IOException e) {
+		System.err.println("BatchCompilerTest2#testBug568802 could not write to current working directory " + currentWorkingDirectoryPath);
+	} finally {
+		new File(libPath).delete();
+	}
+}
+
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CastTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CastTest.java
index 45f4e9e..c979cdf 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CastTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CastTest.java
@@ -3532,6 +3532,44 @@
 	runner.runWarningTest();
 }
 
+public void testBug561167() {
+	if (this.complianceLevel < ClassFileConstants.JDK10)
+		return;
+	Map customOptions = getCompilerOptions();
+	customOptions.put(CompilerOptions.OPTION_ReportUnnecessaryTypeCheck, CompilerOptions.ERROR);
+	runNegativeTest(
+		// test directory preparation
+		true /* flush output directory */,
+		new String[] { /* test files */
+			"X.java",
+			"public class X {\n" +
+			"	public static void main(String[] args) {\n" +
+			"		var s = (String) null;	// Necessary\n" +
+			"		var t = (String) \"hello\";	// UNnecessary\n" +
+			"		var f = (float) 12;			// Necessary\n" +
+			"		var g = (float)f;			// UNnecessary\n" +
+			"	}\n" +
+			"}\n"
+		},
+		// compiler options
+		null /* no class libraries */,
+		customOptions /* custom options */,
+		// compiler results
+		"----------\n" +
+		"1. ERROR in X.java (at line 4)\n" +
+		"	var t = (String) \"hello\";	// UNnecessary\n" +
+		"	        ^^^^^^^^^^^^^^^^\n" +
+		"Unnecessary cast from String to String\n" +
+		"----------\n" +
+		"2. ERROR in X.java (at line 6)\n" +
+		"	var g = (float)f;			// UNnecessary\n" +
+		"	        ^^^^^^^^\n" +
+		"Unnecessary cast from float to float\n" +
+		"----------\n",
+		// javac options
+		JavacTestOptions.Excuse.EclipseWarningConfiguredAsError /* javac test options */);
+}
+
 public static Class testClass() {
 	return CastTest.class;
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
index ff5f727..9cfe8a8 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
@@ -1130,6 +1130,7 @@
 		expectedProblemAttributes.put("UseAssertAsAnIdentifier", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
 		expectedProblemAttributes.put("UseEnumAsAnIdentifier", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
 		expectedProblemAttributes.put("IllegalUseOfUnderscoreAsAnIdentifier", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
+		expectedProblemAttributes.put("ErrorUseOfUnderscoreAsAnIdentifier", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
 		expectedProblemAttributes.put("UsingDeprecatedConstructor", new ProblemAttributes(CategorizedProblem.CAT_DEPRECATION));
 		expectedProblemAttributes.put("UsingDeprecatedField", new ProblemAttributes(CategorizedProblem.CAT_DEPRECATION));
 		expectedProblemAttributes.put("UsingDeprecatedMethod", new ProblemAttributes(CategorizedProblem.CAT_DEPRECATION));
@@ -1263,6 +1264,7 @@
 	    expectedProblemAttributes.put("RecordIllegalStaticModifierForLocalClassOrInterface", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
 	    expectedProblemAttributes.put("RecordIllegalModifierForLocalRecord", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
 	    expectedProblemAttributes.put("LocalStaticsIllegalVisibilityModifierForInterfaceLocalType", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
+	    expectedProblemAttributes.put("IllegalModifierForLocalEnumDeclaration", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
 	    expectedProblemAttributes.put("SealedMissingClassModifier", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
 	    expectedProblemAttributes.put("SealedDisAllowedNonSealedModifierInClass", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
 	    expectedProblemAttributes.put("SealedSuperClassDoesNotPermit", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED));
@@ -2182,6 +2184,7 @@
 		expectedProblemAttributes.put("UseAssertAsAnIdentifier", new ProblemAttributes(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER));
 		expectedProblemAttributes.put("UseEnumAsAnIdentifier", new ProblemAttributes(JavaCore.COMPILER_PB_ENUM_IDENTIFIER));
 		expectedProblemAttributes.put("IllegalUseOfUnderscoreAsAnIdentifier", SKIP);
+		expectedProblemAttributes.put("ErrorUseOfUnderscoreAsAnIdentifier", SKIP);
 		expectedProblemAttributes.put("UsingDeprecatedConstructor", new ProblemAttributes(JavaCore.COMPILER_PB_DEPRECATION));
 		expectedProblemAttributes.put("UsingDeprecatedField", new ProblemAttributes(JavaCore.COMPILER_PB_DEPRECATION));
 		expectedProblemAttributes.put("UsingDeprecatedMethod", new ProblemAttributes(JavaCore.COMPILER_PB_DEPRECATION));
@@ -2314,6 +2317,7 @@
 	    expectedProblemAttributes.put("RecordIllegalParameterNameInCanonicalConstructor",SKIP);
 	    expectedProblemAttributes.put("RecordIllegalExplicitFinalFieldAssignInCompactConstructor",SKIP);
 	    expectedProblemAttributes.put("LocalStaticsIllegalVisibilityModifierForInterfaceLocalType",SKIP);
+	    expectedProblemAttributes.put("IllegalModifierForLocalEnumDeclaration",SKIP);
 	    expectedProblemAttributes.put("SealedMissingClassModifier", SKIP);
 	    expectedProblemAttributes.put("SealedDisAllowedNonSealedModifierInClass", SKIP);
 	    expectedProblemAttributes.put("SealedSuperClassDoesNotPermit", SKIP);
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FieldAccessTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FieldAccessTest.java
index 2a09e1b..bd8fd95 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FieldAccessTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FieldAccessTest.java
@@ -794,6 +794,31 @@
 		"f cannot be resolved or is not a field\n" +
 		"----------\n");
 }
+public void testBug568959_001() {
+	if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // lambda
+	runNegativeTest(
+		new String[] {
+			"X.java",
+			"public class X {\n"+
+			" public void foo(Object o) {\n"+
+			"   I i = () -> {\n"+
+			"     while (o.eq) {\n"+
+			"       // nothing\n"+
+			"     }\n"+
+			"   };\n"+
+			" }\n"+
+			"}\n"+
+			"interface I { \n"+
+			" public abstract void run();\n"+
+			"}"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 4)\n" +
+		"	while (o.eq) {\n" +
+		"	         ^^\n" +
+		"eq cannot be resolved or is not a field\n" +
+		"----------\n");
+}
 public static Class testClass() {
 	return FieldAccessTest.class;
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
index d5ce778..f71d4bd 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -6614,5 +6614,48 @@
 		},
 		output);
 }
+public void testBug561544() {
+	if (this.complianceLevel < ClassFileConstants.JDK11)
+		return;
+	Map customOptions = getCompilerOptions();
+	customOptions.put(JavaCore.COMPILER_PB_UNAVOIDABLE_GENERIC_TYPE_PROBLEMS, JavaCore.DISABLED);
+	runNegativeTest(false,
+		new String[] {
+			"com/bsbportal/music/C2193c.java",
+			"package com.bsbportal.music;\n"
+			+ "\n"
+			+ "public class C2193c {\n"
+			+ "    java.util.List f6088b;\n"
+			+ "    public void onResponse(p594x.C14183r<java.lang.String> rVar) {\n"
+			+ "        mo12821b((java.util.List<java.lang.String>) this.f6088b);\n"
+			+ "    }\n"
+			+ "}\n"
+		},
+		null,
+		customOptions,
+		"----------\n" +
+		"1. WARNING in com\\bsbportal\\music\\C2193c.java (at line 4)\n" +
+		"	java.util.List f6088b;\n" +
+		"	^^^^^^^^^^^^^^\n" +
+		"List is a raw type. References to generic type List<E> should be parameterized\n" +
+		"----------\n" +
+		"2. ERROR in com\\bsbportal\\music\\C2193c.java (at line 5)\n" +
+		"	public void onResponse(p594x.C14183r<java.lang.String> rVar) {\n" +
+		"	                       ^^^^^\n" +
+		"p594x cannot be resolved to a type\n" +
+		"----------\n" +
+		"3. ERROR in com\\bsbportal\\music\\C2193c.java (at line 6)\n" +
+		"	mo12821b((java.util.List<java.lang.String>) this.f6088b);\n" +
+		"	^^^^^^^^\n" +
+		"The method mo12821b(List<String>) is undefined for the type C2193c\n" +
+		"----------\n" +
+		"4. WARNING in com\\bsbportal\\music\\C2193c.java (at line 6)\n" +
+		"	mo12821b((java.util.List<java.lang.String>) this.f6088b);\n" +
+		"	         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+		"Type safety: Unchecked cast from List to List<String>\n" +
+		"----------\n",
+		"", "", null
+	);
+}
 }
 
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
index b3d9f6d..3932bbe 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JavadocTest_15.java
@@ -352,5 +352,123 @@
 			            new String[] {"module-info.java", moduleInfo  }, errorMsg,

 			            JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);

 }

+

+public void test007() {

+	File outputDirectory = new File(OUTPUT_DIR);

+	Util.flushDirectoryContent(outputDirectory);

+

+	String moduleInfo = "" +

+						"/**\n" +

+						" */\n" +

+						"module mod.one { \n" +

+						" exports p;\n" +

+						"}";

+	String I1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * interface I1\n" +

+				" * @see mod.one/ abc\n" +

+				" */\n" +

+				"interface I1 {\n" +

+				"	/**\n" +

+				"	 * Method foo\n" +

+				"    * @return int\n" +

+				"    */\n" +

+				"	public int foo();\n" +

+				"}";

+

+	String P1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * class P1\n" +

+				" * @see mod.one/p.I1 xyz\n" +

+				" */\n" +

+				"public class P1 implements I1 {\n" +

+				"	@Override\n" +

+				"	public int foo() { return 0; }\n" +

+				"}";

+

+	this.runConformTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,

+			            new String[] {"module-info.java", moduleInfo  }, "" );

+}

+

+public void test008() {

+	File outputDirectory = new File(OUTPUT_DIR);

+	Util.flushDirectoryContent(outputDirectory);

+

+	String moduleInfo = "" +

+						"/**\n" +

+						" */\n" +

+						"module mod.one { \n" +

+						" exports p;\n" +

+						"}";

+	String I1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * interface I1\n" +

+				" * {@link mod.one/ abc}\n" +

+				" */\n" +

+				"interface I1 {\n" +

+				"	/**\n" +

+				"	 * Method foo\n" +

+				"    * @return int\n" +

+				"    */\n" +

+				"	public int foo();\n" +

+				"}";

+

+	String P1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * class P1\n" +

+				" * {@link mod.one/p.I1 xyz}\n" +

+				" */\n" +

+				"public class P1 implements I1 {\n" +

+				"	@Override\n" +

+				"	public int foo() { return 0; }\n" +

+				"}";

+

+	this.runConformTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,

+			            new String[] {"module-info.java", moduleInfo  }, "" );

+}

+

+public void test009() {

+	File outputDirectory = new File(OUTPUT_DIR);

+	Util.flushDirectoryContent(outputDirectory);

+

+	String moduleInfo = "" +

+						"/**\n" +

+						" */\n" +

+						"module mod.one { \n" +

+						" exports p;\n" +

+						"}";

+	String I1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * interface I1\n" +

+				" * {@linkplain mod.one/ abc}\n" +

+				" */\n" +

+				"interface I1 {\n" +

+				"	/**\n" +

+				"	 * Method foo\n" +

+				"    * @return int\n" +

+				"    */\n" +

+				"	public int foo();\n" +

+				"}";

+

+	String P1 = "" +

+				"package p;\n" +

+				"/**\n" +

+				" * class P1\n" +

+				" * {@linkplain mod.one/p.I1 xyz}\n" +

+				" */\n" +

+				"public class P1 implements I1 {\n" +

+				"	@Override\n" +

+				"	public int foo() { return 0; }\n" +

+				"}";

+

+	this.runConformTest(new String[] {"p/I1.java", I1 ,"p/P1.java" , P1 } ,

+			            new String[] {"module-info.java", moduleInfo  }, "" );

+}

+

 }

 

diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalStaticsTest_15.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalStaticsTest_15.java
index 9df92de..748c06d 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalStaticsTest_15.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalStaticsTest_15.java
@@ -930,4 +930,83 @@
 		 	"----------\n"
 			);
 	}
+	public void testBug568514LocalEnums_001() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        public enum I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	public enum I {}\n" +
+			"	            ^\n" +
+			"Illegal modifier for local enum I; no explicit modifier is permitted\n" +
+			"----------\n"
+		);
+	}
+	public void testBug568514LocalEnums_002() {
+		Map<String, String> options = getCompilerOptions();
+		options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.DISABLED);
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        public enum I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	public enum I {}\n" +
+			"	            ^\n" +
+			"The member enum I can only be defined inside a top-level class or interface or in a static context\n" +
+			"----------\n",
+			null,
+			true,
+			options
+		);
+	}
+	public void testBug568514LocalEnums_003() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        public enum I {}\n"+
+				"    Zork;\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 4)\n" +
+			"	Zork;\n" +
+			"	^^^^\n" +
+			"Syntax error, insert \"VariableDeclarators\" to complete LocalVariableDeclaration\n" +
+			"----------\n"
+		);
+	}
+	public void testBug568514LocalEnums_004() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        public strictfp enum I {}\n"+
+				"    Zork;\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 4)\n" +
+			"	Zork;\n" +
+			"	^^^^\n" +
+			"Syntax error, insert \"VariableDeclarators\" to complete LocalVariableDeclaration\n" +
+			"----------\n"
+		);
+	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
index 248e45d..9ac6d83 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
@@ -46,7 +46,7 @@
 public class ModuleCompilationTests extends AbstractBatchCompilerTest {
 
 	static {
-//		 TESTS_NAMES = new String[] { "test001" };
+//		 TESTS_NAMES = new String[] { "testRelease565930" };
 		// TESTS_NUMBERS = new int[] { 1 };
 		// TESTS_RANGE = new int[] { 298, -1 };
 	}
@@ -4066,8 +4066,8 @@
 		     "----------\n" +
     		 "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 3)\n" +
     		 "	public java.util.stream.Stream<String> emptyStream() {\n" +
-    		 "	       ^^^^^^^^^^^^^^^^\n" +
-    		 "java.util.stream cannot be resolved to a type\n" +
+    		 "	       ^^^^^^^^^^^^^^^^^^^^^^^\n" +
+    		 "java.util.stream.Stream cannot be resolved to a type\n" +
     		 "----------\n" +
     		 "1 problem (1 error)\n",
 		     true);
@@ -5477,4 +5477,40 @@
 				false,
 				"");
 	}
+	public void testRelease565930_1() throws Exception {
+		this.runConformTest(
+				new String[] {
+					"Dummy.java",
+					"public class Dummy {\n"
+					+ "	boolean b = new String(\"a\").contains(\"b\");\n"
+					+ "}",
+				},
+		     "\"" + OUTPUT_DIR +  File.separator + "Dummy.java\""
+		     + " --release 9 -d \"" + OUTPUT_DIR + "\"",
+		     "",
+		     "",
+		     true);
+	}
+	public void testRelease565930_2() throws Exception {
+		this.runConformTest(
+				new String[] {
+					"Main.java",
+					"public final class Main<T extends Object> {\n"
+					+ "    public void test() {\n"
+					+ "    	final ClassLoader classLoader = this.getClass().getClassLoader();\n"
+					+ "    } \n"
+					+ "}",
+				},
+		     "\"" + OUTPUT_DIR +  File.separator + "Main.java\""
+		     + " --release 9 -d \"" + OUTPUT_DIR + "\"",
+		     "",
+		     "----------\n" +
+    		 "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/Main.java (at line 3)\n" +
+    		 "	final ClassLoader classLoader = this.getClass().getClassLoader();\n" +
+    		 "	                  ^^^^^^^^^^^\n" +
+    		 "The value of the local variable classLoader is not used\n" +
+    		 "----------\n" +
+    		 "1 problem (1 warning)\n",
+		     true);
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
index 8a5b888..20800c7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java
@@ -7362,6 +7362,7 @@
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=421711, [1.8][compiler] '_' as identifier for a lambda parameter should be rejected.
 public void testUnderScoreParameter() {
 		String level = this.complianceLevel >= ClassFileConstants.JDK9 ? "ERROR" : "WARNING";
+		String errorMessage = this.complianceLevel >= ClassFileConstants.JDK9 ? "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" : "\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n";
 		this.runNegativeTest(
 			new String[] {
 					"X.java",
@@ -7380,17 +7381,17 @@
 			"1. ERROR in X.java (at line 6)\n" +
 			"	F f = (int _) -> {\n" +
 			"	           ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			"\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" +
 			"----------\n" +
 			"2. "+ level +" in X.java (at line 8)\n" +
 			"	F f2 = _ -> {};\n" +
 			"	       ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			errorMessage +
 			"----------\n" +
 			"3. ERROR in X.java (at line 8)\n" +
 			"	F f2 = _ -> {};\n" +
 			"	       ^\n" +
-			"\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n" +
+			"\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" +
 			"----------\n"
 		);
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
index 33d955d..07ff8d7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
@@ -70,7 +70,7 @@
 		runner.expectedOutputString = expectedOutput;
 		runner.vmArguments = new String[] {"--enable-preview"};
 		runner.customOptions = customOptions;
-		runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("15");
+		runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("16");
 		runner.runConformTest();
 	}
 	@Override
@@ -2382,7 +2382,7 @@
 		"1. ERROR in X.java (at line 1)\n" +
 		"	record R() {}\n" +
 		"	^^^^^^\n" +
-		"Syntax error on token \"record\", record expected\n" +
+		"Records is a preview feature and disabled by default. Use --enable-preview to enable\n" +
 		"----------\n",
 		null,
 		true,
@@ -2415,6 +2415,27 @@
 		options
 	);
 }
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public void testBug558718_003() {
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_14);
+	options.put(CompilerOptions.OPTION_EnablePreviews, CompilerOptions.DISABLED);
+	this.runNegativeTest(
+	new String[] {
+			"X.java",
+			"record R() {}\n",
+		},
+	"----------\n" +
+	"1. ERROR in X.java (at line 1)\n" +
+	"	record R() {}\n" +
+	"	^^^^^^\n" +
+	"The preview feature Records is only available with source level 16 and above\n" +
+	"----------\n",
+		null,
+		true,
+		options
+	);
+}
 public void testBug56180_001() throws Exception {
 	runConformTest(
 		new String[] {
@@ -6874,7 +6895,7 @@
 		"1. ERROR in X.java (at line 1)\n" +
 		"	record Point(record x, int i) { }\n" +
 		"	^^^^^^\n" +
-		"Syntax error on token \"record\", record expected\n" +
+		"Records is a preview feature and disabled by default. Use --enable-preview to enable\n" +
 		"----------\n" +
 		"2. WARNING in X.java (at line 7)\n" +
 		"	class record {}\n" +
@@ -7667,7 +7688,7 @@
 			"1. ERROR in X.java (at line 3)\n" +
 			"	static enum E {\n" +
 			"	            ^\n" +
-			"A local interface, enum or record E is implicitly static; cannot have explicit static declaration\n" +
+			"Illegal modifier for local enum E; no explicit modifier is permitted\n" +
 			"----------\n");
 }
 public void testBug566063_003() {
@@ -7694,12 +7715,12 @@
 			"1. ERROR in X.java (at line 3)\n" +
 			"	static enum E {\n" +
 			"	            ^\n" +
-			"A local interface, enum or record E is implicitly static; cannot have explicit static declaration\n" +
+			"Illegal modifier for local enum E; no explicit modifier is permitted\n" +
 			"----------\n" +
 			"2. ERROR in X.java (at line 8)\n" +
 			"	static record Bar(E x) implements I{}\n" +
 			"	              ^^^\n" +
-			"A local interface, enum or record Bar is implicitly static; cannot have explicit static declaration\n" +
+			"A local class or interface Bar is implicitly static; cannot have explicit static declaration\n" +
 			"----------\n");
 }
 public void testBug566063_004() {
@@ -7976,4 +7997,71 @@
 			new String[] {"--enable-preview"},
 			getCompilerOptions());
 }
+public void testBug561199_001() {
+	Map<String, String> options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.ERROR);
+	runNegativeTest(
+			new String[] {
+				"R.java",
+				"record R() implements java.io.Serializable {}\n",
+				"X.java",
+				"class X implements java.io.Serializable {}\n"
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	class X implements java.io.Serializable {}\n" +
+			"	      ^\n" +
+			"The serializable class X does not declare a static final serialVersionUID field of type long\n" +
+			"----------\n",
+			null,
+			true,
+			new String[] {"--enable-preview"},
+			options);
+}
+public void testBug568922_001() {
+	runNegativeTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				" public static void main(String[] args) {\n"+
+				"   @SuppressWarnings(\"preview\")\n"+
+				"   record R() {\n"+
+				"     R  {\n"+
+				"       super();\n"+
+				"       System.out.println(\"helo\");\n"+
+				"     }\n"+
+				"   }\n"+
+				"   new R();\n"+
+				" }\n"+
+				"}"
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 6)\n" +
+			"	super();\n" +
+			"	^^^^^^^^\n" +
+			"The body of a compact constructor must not contain an explicit constructor call\n" +
+			"----------\n",
+			null,
+			true,
+			new String[] {"--enable-preview"},
+			getCompilerOptions());
+}
+public void testBug568922_002() {
+	runConformTest(
+		new String[] {
+			"X.java",
+			"public class X {\n"+
+			" public static void main(String[] args) {\n"+
+			"   @SuppressWarnings(\"preview\")\n"+
+			"   record R() {\n"+
+			"     R  {\n"+
+			"       System.out.println(\"helo\");\n"+
+			"     }\n"+
+			"   }\n"+
+			"   new R();\n"+
+			" }\n"+
+			"}"
+		},
+		"helo");
+}
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
index 138f7eb..d51b717 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
@@ -6591,4 +6591,382 @@
 		"The type SQLiteDatabase must implement the inherited abstract method Closeable.close()\n" +
 		"----------\n");
 }
+public void testBug499037_001_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.Closeable;\n" +
+			"import java.io.IOException;\n" +
+			"\n" +
+			"class Y implements Closeable {\n" +
+			"        @Override\n" +
+			"        public void close() throws IOException {\n" +
+			"                // nothing\n" +
+			"        }\n" +
+			"}\n" +
+			"public class X {\n" +
+			"\n" +
+			"        public void foo() throws IOException {\n" +
+			"             final Y y1 = new Y();\n" +
+			"             try (y1) { \n" +
+			"            	 //\n" +
+			"             }\n" +
+			"        } \n" +
+			"        public static void main(String[] args) {\n" +
+			"			System.out.println(\"Done\");\n" +
+			"		}\n" +
+			"} \n"
+		},
+		"",
+		options);
+}
+public void testBug499037_002_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.Closeable;\n" +
+			"import java.io.IOException;\n" +
+			"\n" +
+			"class Y implements Closeable {\n" +
+			"        @Override\n" +
+			"        public void close() throws IOException {\n" +
+			"                // nothing\n" +
+			"        }\n" +
+			"}\n" +
+			"public class X {\n" +
+			"\n" +
+			"        public void foo() throws IOException {\n" +
+			"             Y y1 = new Y();\n" +
+			"             try (y1; final Y y2 = new Y()) { \n" +
+			"            	 //\n" +
+			"             }\n" +
+			"        } \n" +
+			"        public static void main(String[] args) {\n" +
+			"			System.out.println(\"Done\");\n" +
+			"		}\n" +
+			"} \n"
+		},
+		"",
+		options);
+}
+public void testBug499037_003_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.Closeable;\n" +
+			"import java.io.IOException;\n" +
+			"\n" +
+			"public class X { \n" +
+			"    public void foo() throws IOException {\n" +
+			"         Y y1 = new Y();\n" +
+			"         try(y1) { \n" +
+			"             return;\n" +
+			"         }\n" +
+			"    } \n" +
+			"}  \n" +
+			"\n" +
+			"class Y implements Closeable {\n" +
+			"		final int x = 10;\n" +
+			"        @Override\n" +
+			"        public void close() throws IOException {\n" +
+			"                // nothing\n" +
+			"        }\n" +
+			"}"
+		},
+		"",
+		options);
+}
+public void testBug499037_004_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n" +
+			"\n" +
+			"class Z {\n" +
+			"	final Y yz = new Y();\n" +
+			"}\n" +
+			"public class X extends Z {\n" +
+			"	final Y y2 = new Y();\n" +
+			"	\n" +
+			"	public void foo() {\n" +
+			"		try (super.yz; y2)  {\n" +
+			"			System.out.println(\"In Try\");\n" +
+			"		} catch (IOException e) {\n" +
+			"			\n" +
+			"		}finally { \n" +
+			"		}\n" +
+			"	}\n" +
+			"	public static void main(String[] args) {\n" +
+			"		new X().foo();\n" +
+			"	}\n" +
+			"}\n" +
+			"class Y implements AutoCloseable {\n" +
+			"	@Override\n" +
+			"	public void close() throws IOException {\n" +
+			"		System.out.println(\"Closed\");\n" +
+			"	} \n" +
+			"}  \n"
+		},
+		"",
+		options);
+}
+public void testBug499037_005_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"	void test(boolean b) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		if (b) {\n"+
+			"			try (y) {}\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 5)\n" +
+		"	Y y = new Y();\n" +
+		"	  ^\n" +
+		"Potential resource leak: \'y\' may not be closed\n" +
+		"----------\n",
+		options);
+}
+// non-empty finally block - takes a different route
+public void testBug499037_006_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"	void test(boolean b) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		if (b) {\n"+
+			"			try (y;Y y2 = new Y();) { \n"+
+			"			} finally {\n"+
+			"			  System.out.println(\"hello\");\n"+
+			"			}\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 5)\n" +
+		"	Y y = new Y();\n" +
+		"	  ^\n" +
+		"Potential resource leak: \'y\' may not be closed\n" +
+		"----------\n",
+		options);
+}
+public void testBug499037_007_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"	void test(boolean b) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		if (b) {\n"+
+			"			try (y) { \n"+
+			"			    // nothing \n"+
+			"			}\n"+
+			"		}\n"+
+			"		else {\n"+
+			"			y.close();\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"",
+		options);
+}
+public void testBug499037_008_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"	void test(boolean b, Y yDash) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		if (b) {\n"+
+			"			try (y; yDash) { \n"+
+			"			    // nothing \n"+
+			"			}\n"+
+			"		}\n"+
+			"		else {\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 5)\n" +
+		"	Y y = new Y();\n" +
+		"	  ^\n" +
+		"Potential resource leak: \'y\' may not be closed\n" +
+		"----------\n",
+		options);
+}
+public void testBug499037_009_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"private void foo(Y y) {}\n" +
+			"	void test(boolean b) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		if (b) {\n"+
+			"			try (y) { \n"+
+			"			    // nothing \n"+
+			"			}\n"+
+			"		}\n"+
+			"		else {\n"+
+			"			foo(y);\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 6)\n" +
+		"	Y y = new Y();\n" +
+		"	  ^\n" +
+		"Potential resource leak: \'y\' may not be closed\n" +
+		"----------\n",
+		options);
+}
+public void testBug499037_010_since_9() {
+	if (this.complianceLevel < ClassFileConstants.JDK9) return;
+	Map options = getCompilerOptions();
+	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+	options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+	options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+	runLeakTest(
+		new String[] {
+			"X.java",
+			"import java.io.IOException;\n"+
+			"\n"+
+			"public class X {\n"+
+			"private Y foo(Y y) {return y;}\n" +
+			"	void test(boolean b) throws IOException {\n"+
+			"		Y y = new Y();\n"+
+			"		Y yy = foo(y);\n"+
+			"		if (b) {\n"+
+			"			try (y;yy) { \n"+
+			"			    // do nothing \n"+
+			"			}\n"+
+			"		}\n"+
+			"		else {\n"+
+			"			// do nothing\n"+
+			"		}\n"+
+			"	}\n"+
+			"}\n"+
+			"\n"+
+			"class Y implements AutoCloseable {\n"+
+			"	@Override\n"+
+			"	public void close() throws IOException {\n"+
+			"	}\n"+
+			"}\n"
+		},
+		"----------\n" +
+		"1. ERROR in X.java (at line 6)\n" +
+		"	Y y = new Y();\n" +
+		"	  ^\n" +
+		"Potential resource leak: \'y\' may not be closed\n" +
+		"----------\n" +
+		"2. ERROR in X.java (at line 7)\n" +
+		"	Y yy = foo(y);\n" +
+		"	  ^^\n" +
+		"Potential resource leak: \'yy\' may not be closed\n" +
+		"----------\n",
+		options);
+}
 }
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
index 1b8d662..ffd36f3 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
@@ -35,7 +35,7 @@
 	static {
 //		TESTS_NUMBERS = new int [] { 40 };
 //		TESTS_RANGE = new int[] { 1, -1 };
-//		TESTS_NAMES = new String[] { "testBug566979","testBug566980", "testBug566846"};
+//		TESTS_NAMES = new String[] { "testBug568428"};
 	}
 
 	public static Class<?> testClass() {
@@ -73,7 +73,7 @@
 		runner.expectedOutputString = expectedOutput;
 		runner.vmArguments = new String[] {"--enable-preview"};
 		runner.customOptions = customOptions;
-		runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("15");
+		runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("16");
 		runner.runConformTest();
 	}
 	@Override
@@ -249,7 +249,7 @@
 			"2. ERROR in X.java (at line 1)\n" +
 			"	sealed public sealed class X {\n" +
 			"	                           ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares X as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
 			"----------\n");
 	}
 	public void testBug562715_006() {
@@ -266,7 +266,7 @@
 			"1. ERROR in X.java (at line 1)\n" +
 			"	public sealed class X {\n" +
 			"	                    ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares X as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
 			"----------\n" +
 			"2. ERROR in X.java (at line 2)\n" +
 			"	public static sealed void main(String[] args){\n" +
@@ -331,7 +331,7 @@
 			"1. ERROR in X.java (at line 1)\n" +
 			"	public sealed class X permits {\n" +
 			"	                    ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares X as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
 			"----------\n" +
 			"2. ERROR in X.java (at line 1)\n" +
 			"	public sealed class X permits {\n" +
@@ -345,7 +345,7 @@
 			"----------\n");
 	}
 	// TODO : Enable after error flag code implemented
-	public void _testBug562715_011() {
+	public void testBug562715_011() {
 		this.runNegativeTest(
 			new String[] {
 				"X.java",
@@ -357,13 +357,23 @@
 			"}",
 			},
 			"----------\n" +
-			"1. ERROR in X.java (at line 2)\n" +
-			"	EXPECTED ERROR IN RECORD\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	sealed enum Natural {ONE, TWO}\n" +
+			"	            ^^^^^^^\n" +
+			"Illegal modifier for the enum Natural; only public is permitted\n" +
+			"----------\n" +
+			"2. ERROR in X.java (at line 2)\n" +
+			"	public sealed class X {\n" +
+			"	                    ^\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
+			"----------\n" +
+			"3. ERROR in X.java (at line 3)\n" +
+			"	public static sealed void main(String[] args){\n" +
 			"	              ^^^^^^\n" +
 			"Syntax error on token \"sealed\", static expected\n" +
 			"----------\n");
 	}
-	public void _testBug562715_xxx() {
+	public void testBug562715_xxx() {
 		this.runNegativeTest(
 			new String[] {
 				"X.java",
@@ -375,8 +385,18 @@
 			"}",
 			},
 			"----------\n" +
-			"1. ERROR in X.java (at line 2)\n" +
-			"	EXPECTED ERROR IN RECORD\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	sealed record R() {}\n" +
+			"	              ^\n" +
+			"Illegal modifier for the record R; only public, final and strictfp are permitted\n" +
+			"----------\n" +
+			"2. ERROR in X.java (at line 2)\n" +
+			"	public sealed class X {\n" +
+			"	                    ^\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
+			"----------\n" +
+			"3. ERROR in X.java (at line 3)\n" +
+			"	public static sealed void main(String[] args){\n" +
 			"	              ^^^^^^\n" +
 			"Syntax error on token \"sealed\", static expected\n" +
 			"----------\n");
@@ -435,7 +455,7 @@
 			"1. ERROR in p1\\A.java (at line 2)\n" +
 			"	public sealed class A extends X{}\n" +
 			"	                    ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares A as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares A as its direct superclass or superinterface\n" +
 			"----------\n" +
 			"2. ERROR in p1\\A.java (at line 2)\n" +
 			"	public sealed class A extends X{}\n" +
@@ -635,7 +655,7 @@
 			"1. ERROR in p2\\Y.java (at line 2)\n" +
 			"	public final class Y implements p1.X{}\n" +
 			"	                                ^^^^\n" +
-			"The type Y extending a sealed class X should be a permitted subtype of X\n" +
+			"The type Y that implements a sealed interface X should be a permitted subtype of X\n" +
 			"----------\n");
 	}
 	public void testBug563806_013() {
@@ -680,7 +700,7 @@
 			"2. ERROR in p2\\Y.java (at line 2)\n" +
 			"	public interface Y extends p1.X{}\n" +
 			"	                           ^^^^\n" +
-			"The type Y extending a sealed interface X should be a permitted subtype of X\n" +
+			"The type Y that extends a sealed interface X should be a permitted subtype of X\n" +
 			"----------\n");
 	}
 	public void testBug563806_015() {
@@ -1040,7 +1060,7 @@
 			"1. ERROR in p1\\X.java (at line 2)\n" +
 			"	public sealed non-sealed interface X {\n" +
 			"	                                   ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares X as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
 			"----------\n" +
 			"2. ERROR in p1\\X.java (at line 2)\n" +
 			"	public sealed non-sealed interface X {\n" +
@@ -1109,11 +1129,6 @@
 			"	sealed class Y{}\n" +
 			"	             ^\n" +
 			"Illegal modifier for the local class Y; only abstract or final is permitted\n" +
-			"----------\n" +
-			"2. ERROR in p1\\X.java (at line 4)\n" +
-			"	sealed class Y{}\n" +
-			"	             ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares Y as its direct superclass\n" +
 			"----------\n");
 	}
 	public void testBug563806_037() {
@@ -1150,11 +1165,6 @@
 			"	non-sealed sealed class Y{}\n" +
 			"	                        ^\n" +
 			"Illegal modifier for the local class Y; only abstract or final is permitted\n" +
-			"----------\n" +
-			"2. ERROR in p1\\X.java (at line 4)\n" +
-			"	non-sealed sealed class Y{}\n" +
-			"	                        ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares Y as its direct superclass\n" +
 			"----------\n");
 	}
 	public void testBug563806_039() {
@@ -1173,7 +1183,7 @@
 			"1. ERROR in p1\\X.java (at line 2)\n" +
 			"	sealed class A{}\n" +
 			"	             ^\n" +
-			"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares A as its direct superclass\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares A as its direct superclass or superinterface\n" +
 			"----------\n" +
 			"2. ERROR in p1\\X.java (at line 5)\n" +
 			"	class Y extends A{}\n" +
@@ -1302,7 +1312,7 @@
 				"1. ERROR in p1\\X.java (at line 3)\n" +
 				"	sealed class Y extends X {}\n" +
 				"	             ^\n" +
-				"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares Y as its direct superclass\n" +
+				"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares Y as its direct superclass or superinterface\n" +
 				"----------\n");
 	}
 	// Test that implicit permitted member type with explicit permits clause
@@ -1337,7 +1347,7 @@
 				"1. ERROR in p1\\X.java (at line 2)\n" +
 				"	sealed interface SI {}\n" +
 				"	                 ^^\n" +
-				"Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares SI as its direct superclass\n" +
+				"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares SI as its direct superclass or superinterface\n" +
 				"----------\n");
 	}
 	public void testBug564450_001() throws IOException, ClassFormatException {
@@ -2851,7 +2861,7 @@
 			"1. ERROR in X.java (at line 1)\n" +
 			"	record X(permits p) {\n" +
 			"	^^^^^^\n" +
-			"Syntax error on token \"record\", record expected\n" +
+			"Records is a preview feature and disabled by default. Use --enable-preview to enable\n" +
 			"----------\n",
 			null,
 			true,
@@ -4574,7 +4584,7 @@
 			"1. ERROR in X.java (at line 1)\n" +
 			"	record X(sealed p) {\n" +
 			"	^^^^^^\n" +
-			"Syntax error on token \"record\", record expected\n" +
+			"Records is a preview feature and disabled by default. Use --enable-preview to enable\n" +
 			"----------\n",
 			null,
 			true,
@@ -5414,4 +5424,97 @@
 			options
 		);
 	}
+	public void testBug568428_001() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        sealed interface I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	sealed interface I {}\n" +
+			"	                 ^\n" +
+			"Illegal modifier for the local interface I; abstract and strictfp are the only modifiers allowed explicitly \n" +
+			"----------\n"
+		);
+	}
+	public void testBug568428_002() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        non-sealed interface I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	non-sealed interface I {}\n" +
+			"	                     ^\n" +
+			"Illegal modifier for the local interface I; abstract and strictfp are the only modifiers allowed explicitly \n" +
+			"----------\n"
+		);
+	}
+	public void testBug568514_001() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        sealed enum I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	sealed enum I {}\n" +
+			"	            ^\n" +
+			"Illegal modifier for local enum I; no explicit modifier is permitted\n" +
+			"----------\n"
+		);
+	}
+	public void testBug568514_002() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"class X {\n"+
+				"    public void foo() {\n" +
+				"        non-sealed enum I {}\n"+
+				"    }\n"+
+				"}\n",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 3)\n" +
+			"	non-sealed enum I {}\n" +
+			"	                ^\n" +
+			"Illegal modifier for local enum I; no explicit modifier is permitted\n" +
+			"----------\n"
+		);
+	}
+	public void testBug568758_001() {
+		this.runNegativeTest(
+			new String[] {
+				"X.java",
+				"public sealed interface X{}\n",
+				"Y.java",
+				"public final class Y implements X{}",
+			},
+			"----------\n" +
+			"1. ERROR in X.java (at line 1)\n" +
+			"	public sealed interface X{}\n" +
+			"	                        ^\n" +
+			"Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" +
+			"----------\n" +
+			"----------\n" +
+			"1. ERROR in Y.java (at line 1)\n" +
+			"	public final class Y implements X{}\n" +
+			"	                                ^\n" +
+			"The type Y that implements a sealed interface X should be a permitted subtype of X\n" +
+			"----------\n");
+	}
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_15Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_15Test.java
index a7e1d89..92b8520 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_15Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_15Test.java
@@ -557,6 +557,54 @@
 		}
 	}
 
+	public void testRecord012() throws CoreException {
+		if (!isJRE15) {
+			System.err.println("Test "+getName()+" requires a JRE 15");
+			return;
+		}
+		String contents =
+			"public record X(int myComp) {\n" +
+			"		public void foo() {\n" +
+			"			System.out.println(\"no error\");\n" +
+			"		}\n" +
+			"\n" +
+			"}\n";
+		this.workingCopy = getWorkingCopy("/Converter_15/src/X.java", true/*resolve*/);
+		IJavaProject javaProject = this.workingCopy.getJavaProject();
+		String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true);
+		try {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
+			javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+			ASTNode node = buildAST(
+				contents,
+				this.workingCopy);
+			assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType());
+			CompilationUnit compilationUnit = (CompilationUnit) node;
+			assertProblemsSize(compilationUnit, 0);
+			List<AbstractTypeDeclaration> types = compilationUnit.types();
+			assertEquals("No. of Types is not 1", types.size(), 1);
+			AbstractTypeDeclaration type = types.get(0);
+			assertTrue("type not a Record", type instanceof RecordDeclaration);
+			RecordDeclaration recDecl = (RecordDeclaration)type;
+			MethodDeclaration[] methods = recDecl.getMethods();
+			assertEquals("No. of methods is not 1", methods.length, 1);
+			ITypeBinding typeBinding = type.resolveBinding();
+			assertNotNull("typeBinding is null", typeBinding);
+			IMethodBinding[] mBindings = typeBinding.getDeclaredMethods();
+			assertEquals("No. of declared methods is not 6", mBindings.length, 6);
+			for (IMethodBinding mBinding : mBindings) {
+				if (mBinding.getName().equals("X") || mBinding.getName().equals("foo")) {
+					assertFalse("foo is not a synthetic method", mBinding.isSyntheticRecordMethod());
+				} else {
+					assertTrue("expected a synthetic method", mBinding.isSyntheticRecordMethod());
+				}
+			}
+
+		} finally {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
+		}
+	}
+
 	public void testClass001() throws CoreException {
 		if (!isJRE15) {
 			System.err.println("Test "+getName()+" requires a JRE 15");
@@ -762,7 +810,7 @@
 		assertEquals("wrong line number", 9, compilationUnit.getLineNumber(node.getStartPosition()));
 	}
 
-	public void _testPatternInstanceOfExpression001() throws JavaModelException {
+	public void testPatternInstanceOfExpression001() throws JavaModelException {
 		if (!isJRE15) {
 			System.err.println("Test "+getName()+" requires a JRE 15");
 			return;
@@ -797,14 +845,14 @@
 				checkSourceRange(expression, "o instanceof String s", contents);
 				assertEquals("Not an instanceof expression", ASTNode.INSTANCEOF_EXPRESSION, expression.getNodeType());
 				InstanceofExpression instanceofExpression = (InstanceofExpression) expression;
-				SingleVariableDeclaration var = instanceofExpression.getPatternVariable();
-				checkSourceRange(var, "String s", contents);
+				SimpleName var = instanceofExpression.getPatternVariable();
+				checkSourceRange(var, "s", contents);
 			}finally {
 				javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
 			}
 	}
 
-	public void _testPatternInstanceOfExpression002() throws JavaModelException {
+	public void testPatternInstanceOfExpression002() throws JavaModelException {
 		if (!isJRE15) {
 			System.err.println("Test "+getName()+" requires a JRE 15");
 			return;
@@ -840,13 +888,57 @@
 				checkSourceRange(expression, "o instanceof String", contents);
 				assertEquals("Not an instanceof expression", ASTNode.INSTANCEOF_EXPRESSION, expression.getNodeType());
 				InstanceofExpression instanceofExpression = (InstanceofExpression) expression;
-				SingleVariableDeclaration var = instanceofExpression.getPatternVariable();
+				SimpleName var = instanceofExpression.getPatternVariable();
 				assertNull(var);
 			}finally {
 				javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
 			}
 	}
 
+	public void testPatternInstanceOfExpression003() throws JavaModelException {
+		if (!isJRE15) {
+			System.err.println("Test "+getName()+" requires a JRE 15");
+			return;
+		}
+		String contents =
+				"public class X {\n" +
+						"	public String test001(Object o) {\n" +
+						"		if (o instanceof String s){\n" +
+						"    		System.out.println(s);\n" +
+						"			return s;\n" +
+						"		}\n" +
+						"		return null;\n" +
+						"	}\n" +
+						"}" ;
+		this.workingCopy = getWorkingCopy("/Converter_15/src/X.java", true/*resolve*/);
+		IJavaProject javaProject = this.workingCopy.getJavaProject();
+		String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true);
+		try {
+				javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
+				javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+
+				ASTNode node = buildAST(
+						contents,
+						this.workingCopy);
+				assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType());
+				CompilationUnit compilationUnit = (CompilationUnit) node;
+				assertProblemsSize(compilationUnit, 0);
+				node = getASTNode(compilationUnit, 0, 0, 0);
+				assertEquals("Not an if statement", ASTNode.IF_STATEMENT, node.getNodeType());
+				IfStatement ifStatement = (IfStatement) node;
+				Expression expression = ifStatement.getExpression();
+				checkSourceRange(expression, "o instanceof String s", contents);
+				assertEquals("Not an instanceof expression", ASTNode.INSTANCEOF_EXPRESSION, expression.getNodeType());
+				InstanceofExpression instanceofExpression = (InstanceofExpression) expression;
+				SimpleName var = instanceofExpression.getPatternVariable();
+				checkSourceRange(var, "s", contents);
+				String instanceofExpressionString = instanceofExpression.toString();
+				assertEquals("o instanceof String s", instanceofExpressionString);
+			}finally {
+				javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
+			}
+	}
+
 	public void testSealed001() throws CoreException {
 		if (!isJRE15) {
 			System.err.println("Test "+getName()+" requires a JRE 15");
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaElementDeltaTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaElementDeltaTests.java
index bad8abb..0c3cf05 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaElementDeltaTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaElementDeltaTests.java
@@ -226,7 +226,7 @@
 /*
  * Ensures that adding a library entry for an existing external library folder triggers the correct delta
  */
-public void testAddExternalLibFolder1() throws CoreException {
+public void _testAddExternalLibFolder1() throws CoreException {
 	try {
 		IJavaProject p = createJavaProject("P");
 		createExternalFolder("externalLib");
@@ -251,7 +251,7 @@
 /*
  * Ensures that adding a library entry for a non-existing external library folder triggers the correct delta
  */
-public void testAddExternalLibFolder2() throws CoreException {
+public void _testAddExternalLibFolder2() throws CoreException {
 	try {
 		IJavaProject p = createJavaProject("P");
 		refresh(p);
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchNameEnvironmentTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchNameEnvironmentTest.java
new file mode 100644
index 0000000..0ad2678
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchNameEnvironmentTest.java
@@ -0,0 +1,137 @@
+package org.eclipse.jdt.core.tests.model;
+
+import static java.util.stream.Collectors.toCollection;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
+import org.eclipse.jdt.internal.core.search.matching.ClasspathSourceDirectory;
+import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment;
+
+import junit.framework.Test;
+
+public class JavaSearchNameEnvironmentTest extends ModifyingResourceTests {
+
+	static {
+		//NameLookup.VERBOSE = true;
+	}
+
+	static class JavaSearchNameEnvironmentUnderTest extends JavaSearchNameEnvironment {
+		public JavaSearchNameEnvironmentUnderTest(IJavaProject javaProject, ICompilationUnit[] copies) {
+			super(javaProject, copies);
+		}
+		public LinkedHashSet<ClasspathLocation> getLocationSet() {
+			return super.locationSet;
+		}
+		@Override
+		public Iterable<ClasspathLocation> getLocationsFor(String moduleName, String qualifiedPackageName) {
+			return super.getLocationsFor(moduleName, qualifiedPackageName);
+		}
+		public LinkedHashSet<ClasspathLocation> getAllIndexedLocations() {
+			return super.packageNameToClassPathLocations.values().stream().flatMap(Collection::stream).collect(toCollection(LinkedHashSet::new));
+		}
+		@Override
+		public void addProjectClassPath(JavaProject javaProject) {
+			super.addProjectClassPath(javaProject);
+		}
+	}
+
+	private IJavaProject p1;
+	private IJavaProject p2;
+
+	public JavaSearchNameEnvironmentTest(String name) {
+		super(name);
+		this.endChar = "";
+	}
+
+	public static Test suite() {
+		return buildModelTestSuite(JavaSearchNameEnvironmentTest.class);
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		this.p1 = setUpJavaProject("JavaSearchMultipleProjects1");
+		this.p2 = setUpJavaProject("JavaSearchMultipleProjects2");
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		try {
+			deleteProject(this.p1.getElementName());
+			deleteProject(this.p2.getElementName());
+		} finally {
+			super.tearDown();
+		}
+	}
+
+	public void testLocationsAreEqual() throws CoreException {
+		JavaSearchNameEnvironmentUnderTest nameEnvironment = newJavaSearchEnvironment(this.p1, this.p2);
+
+		LinkedHashSet<ClasspathLocation> locationSet = nameEnvironment.getLocationSet();
+		LinkedHashSet<ClasspathLocation> allIndexedLocations = nameEnvironment.getAllIndexedLocations();
+
+		for (ClasspathLocation cp : locationSet) {
+			assertTrue("index must contain: " + cp, allIndexedLocations.contains(cp));
+		}
+	}
+
+	public void testWorkingCopies() throws CoreException {
+
+		this.workingCopies = new ICompilationUnit[3];
+		this.workingCopies[0] = getWorkingCopy("/JavaSearchMultipleProjects2/src/b88300/SubClass.java",
+				"package b88300;\n" +
+				"public class SubClass extends SuperClass {\n" +
+				"	private void aMethod(String x) {\n" +
+				"	}\n" +
+				"	public void aMethod(Object x) {\n" +
+				"	}\n" +
+				"}\n"
+			);
+			this.workingCopies[1] = getWorkingCopy("/JavaSearchMultipleProjects2/src/b88300/SuperClass.java",
+				"package b88300;\n" +
+				"public class SuperClass {\n" +
+				"    public void aMethod(Object x) {\n" +
+				"    }\n" +
+				"}\n"
+				);
+			this.workingCopies[2] = getWorkingCopy("/JavaSearchMultipleProjects2/src/b88300/User.java",
+				"package b88300;\n" +
+				"public class User {\n" +
+				"    public void methodUsingSubClassMethod() {\n" +
+				"        SuperClass user = new SubClass();\n" +
+				"        user.aMethod(new Object());\n" +
+				"    }\n" +
+				"}\n"
+			);
+
+		JavaSearchNameEnvironmentUnderTest nameEnvironment = newJavaSearchEnvironment(this.p2, this.p1);
+
+		Iterable<ClasspathLocation> locationsForPackage = nameEnvironment.getLocationsFor(null, "b88300");
+		assertNotNull(locationsForPackage);
+		assertTrue(locationsForPackage.iterator().hasNext());
+		ClasspathLocation cp = locationsForPackage.iterator().next();
+		assertTrue(cp instanceof ClasspathSourceDirectory);
+
+		char[][] packageName = new char[][] { "b88300".toCharArray() };
+		assertNotNull("Type User not found!", nameEnvironment.findType("User".toCharArray(), packageName));
+		assertNotNull("Type SuperClass not found!", nameEnvironment.findType("SuperClass".toCharArray(), packageName));
+		assertNotNull("Type SubClass not found!", nameEnvironment.findType("SubClass".toCharArray(), packageName));
+	}
+
+	private JavaSearchNameEnvironmentUnderTest newJavaSearchEnvironment(IJavaProject first, IJavaProject... remaining) {
+		JavaSearchNameEnvironmentUnderTest env = new JavaSearchNameEnvironmentUnderTest(first, this.workingCopies);
+		if(remaining != null) {
+			for (int i = 0; i < remaining.length; i++) {
+				env.addProjectClassPath((JavaProject) remaining[i]);
+			}
+		}
+		return env;
+	}
+}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
index 9ac6153..0bad63d 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
@@ -706,9 +706,9 @@
 			IJavaProject project = setUpJavaProject("ConvertToModule");
 			Map<String, String> options = new HashMap<>();
 			// Make sure the new options map doesn't reset.
-			options.put(CompilerOptions.OPTION_Compliance, "9");
-			options.put(CompilerOptions.OPTION_Source, "9");
-			options.put(CompilerOptions.OPTION_TargetPlatform, "9");
+			options.put(CompilerOptions.OPTION_Compliance, "10");
+			options.put(CompilerOptions.OPTION_Source, "10");
+			options.put(CompilerOptions.OPTION_TargetPlatform, "10");
 			options.put(CompilerOptions.OPTION_Release, "enabled");
 			project.setOptions(options);
 			project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java
index bb234e8..6eec1fb 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java
@@ -74,6 +74,7 @@
 		allClasses.add(MatchingRegionsTest.class);
 		allClasses.add(JavaIndexTests.class);
 		allClasses.add(Bug376673Test.class);
+		allClasses.add(JavaSearchNameEnvironmentTest.class);
 
 		// Reset forgotten subsets of tests
 		TestCase.TESTS_PREFIX = null;
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingInstanceOfPatternExpressionTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingInstanceOfPatternExpressionTest.java
index ce33099..6d74c17 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingInstanceOfPatternExpressionTest.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingInstanceOfPatternExpressionTest.java
@@ -26,7 +26,6 @@
 import org.eclipse.jdt.core.dom.InstanceofExpression;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
 import org.eclipse.jdt.core.dom.SimpleType;
-import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
 
@@ -90,10 +89,7 @@
 			instanceOfExpression.setLeftOperand(ast.newSimpleName("o"));//$NON-NLS-1$
 			SimpleType simpleType = ast.newSimpleType(ast.newSimpleName("String"));//$NON-NLS-1$
 			instanceOfExpression.setRightOperand(simpleType);
-			SingleVariableDeclaration patternVariable = ast.newSingleVariableDeclaration();
-			patternVariable.setName(ast.newSimpleName("s"));
-			patternVariable.setType(ast.newSimpleType(ast.newSimpleName("String")));//$NON-NLS-1$
-			instanceOfExpression.setPatternVariable(patternVariable);
+			instanceOfExpression.setPatternVariable(ast.newSimpleName("s"));
 			ifStatement.setExpression(instanceOfExpression);
 			ifStatement.setThenStatement(ast.newEmptyStatement());
 			rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertLast(ifStatement, null);
@@ -113,4 +109,56 @@
 		assertEqualString(preview, buf.toString());
 
 	}
+
+	@SuppressWarnings({ "rawtypes", "deprecation" })
+	public void test002() throws Exception {
+		if (this.apiLevel != 15) {
+			System.err.println("Test "+getName()+" requires a JRE 15");
+			return;
+		}
+		IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("public class X {\n");
+		buf.append("    void foo(Object o) {\n");
+		buf.append("        if (o instanceof String s)\n");
+		buf.append("            ;\n");
+		buf.append(" 	}\n");
+		buf.append("}\n");
+		ICompilationUnit cu= pack1.createCompilationUnit("X.java", buf.toString(), false, null);
+
+		CompilationUnit astRoot= createAST(cu);
+		ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST());
+
+		AST ast= astRoot.getAST();
+
+		assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0);
+		TypeDeclaration type= findTypeDeclaration(astRoot, "X");
+		MethodDeclaration methodDecl= findMethodDeclaration(type, "foo");
+		Block block= methodDecl.getBody();
+		List blockStatements= block.statements();
+		assertTrue("Number of statements not 1", blockStatements.size() == 1);
+		{ // add InstanceOfPattern expression
+
+			IfStatement ifStatement = (IfStatement)blockStatements.get(0);
+			InstanceofExpression instanceOfExpression = (InstanceofExpression)ifStatement.getExpression();
+			rewrite.set(instanceOfExpression, InstanceofExpression.PATTERN_VARIABLE_PROPERTY, ast.newSimpleName("str1"), null);
+
+		}
+
+		String preview= evaluateRewrite(cu, rewrite);
+
+		buf= new StringBuffer();
+		buf.append("package test1;\n");
+		buf.append("public class X {\n");
+		buf.append("    void foo(Object o) {\n");
+		buf.append("        if (o instanceof String str1)\n");
+		buf.append("            ;\n");
+		buf.append(" 	}\n");
+		buf.append("}\n");
+
+		assertEqualString(preview, buf.toString());
+
+	}
 }
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java
index a848b11..3e67ec9 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018, 2019 IBM Corporation.
+ * Copyright (c) 2018, 2020 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v2.0
  * which accompanies this distribution, and is available at
@@ -210,52 +210,55 @@
 	}
 	@Override
 	public synchronized char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) {
-		// Ignore moduleName as this has nothing to do with modules (as of now)
-		if (this.packageCache != null)
-			return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
-
-		this.packageCache = new HashSet<>(41);
-		this.packageCache.add(Util.EMPTY_STRING);
-		List<String> sub = new ArrayList<>();
-		try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
-			for (final java.nio.file.Path subdir: stream) {
-				String rel = JRTUtil.sanitizedFileName(subdir);
-				if (rel.contains(this.releaseInHex)) {
-					sub.add(rel);
-				} else {
-					continue;
-				}
-				Files.walkFileTree(subdir, new FileVisitor<java.nio.file.Path>() {
-					@Override
-					public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
-						if (dir.getNameCount() <= 1)
+		if (this.packageCache == null) {
+			this.packageCache = new HashSet<>(41);
+			this.packageCache.add(Util.EMPTY_STRING);
+			List<String> sub = new ArrayList<>();
+			try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
+				for (final java.nio.file.Path subdir: stream) {
+					String rel = JRTUtil.sanitizedFileName(subdir);
+					if (rel.contains(this.releaseInHex)) {
+						sub.add(rel);
+					} else {
+						continue;
+					}
+					Files.walkFileTree(subdir, new FileVisitor<java.nio.file.Path>() {
+						@Override
+						public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
+							if (dir.getNameCount() <= 1)
+								return FileVisitResult.CONTINUE;
+							Path relative = dir.subpath(1, dir.getNameCount());
+							addToPackageCache(relative.toString(), false);
 							return FileVisitResult.CONTINUE;
-						Path relative = dir.subpath(1, dir.getNameCount());
-						addToPackageCache(relative.toString(), false);
-						return FileVisitResult.CONTINUE;
-					}
+						}
 
-					@Override
-					public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException {
-						return FileVisitResult.CONTINUE;
-					}
+						@Override
+						public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException {
+							return FileVisitResult.CONTINUE;
+						}
 
-					@Override
-					public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException {
-						return FileVisitResult.CONTINUE;
-					}
+						@Override
+						public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException {
+							return FileVisitResult.CONTINUE;
+						}
 
-					@Override
-					public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
-						return FileVisitResult.CONTINUE;
-					}
-				});
+						@Override
+						public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
+							return FileVisitResult.CONTINUE;
+						}
+					});
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+				// Rethrow
 			}
-		} catch (IOException e) {
-			e.printStackTrace();
-			// Rethrow
+			this.subReleases = sub.toArray(new String[sub.size()]);
 		}
-		this.subReleases = sub.toArray(new String[sub.size()]);
+		if (moduleName == null) {
+			// Delegate to the boss, even if it means inaccurate error reporting at times
+			List<String> mods = JRTUtil.getModulesDeclaringPackage(this.file, qualifiedPackageName, moduleName);
+			return CharOperation.toCharArrays(mods);
+		}
 		return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
 	}
 	@Override
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java
index d49d5f7..a4ba1c0 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 IBM Corporation.
+ * Copyright (c) 2019, 2020 IBM Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v2.0
  * which accompanies this distribution, and is available at
@@ -30,6 +30,7 @@
 import java.util.Map;
 import java.util.function.Function;
 
+import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
@@ -254,51 +255,54 @@
 	}
 	@Override
 	public synchronized char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) {
-		// Ignore moduleName as this has nothing to do with modules (as of now)
-		if (this.packageCache != null)
-			return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
-
-		this.packageCache = new HashSet<>(41);
-		this.packageCache.add(Util.EMPTY_STRING);
-		try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
-			for (final java.nio.file.Path subdir: stream) {
-				String rel = JRTUtil.sanitizedFileName(subdir);
-				if (!rel.contains(this.releaseInHex)) {
-					continue;
-				}
-				try (DirectoryStream<java.nio.file.Path> stream2 = Files.newDirectoryStream(subdir)) {
-					for (final java.nio.file.Path subdir2: stream2) {
-						Files.walkFileTree(subdir2, new FileVisitor<java.nio.file.Path>() {
-							@Override
-							public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
-								if (dir.getNameCount() <= 2)
+		if (this.packageCache == null) {
+			this.packageCache = new HashSet<>(41);
+			this.packageCache.add(Util.EMPTY_STRING);
+			try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
+				for (final java.nio.file.Path subdir: stream) {
+					String rel = JRTUtil.sanitizedFileName(subdir);
+					if (!rel.contains(this.releaseInHex)) {
+						continue;
+					}
+					try (DirectoryStream<java.nio.file.Path> stream2 = Files.newDirectoryStream(subdir)) {
+						for (final java.nio.file.Path subdir2: stream2) {
+							Files.walkFileTree(subdir2, new FileVisitor<java.nio.file.Path>() {
+								@Override
+								public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
+									if (dir.getNameCount() <= 2)
+										return FileVisitResult.CONTINUE;
+									Path relative = dir.subpath(2, dir.getNameCount());
+									addToPackageCache(relative.toString(), false);
 									return FileVisitResult.CONTINUE;
-								Path relative = dir.subpath(2, dir.getNameCount());
-								addToPackageCache(relative.toString(), false);
-								return FileVisitResult.CONTINUE;
-							}
+								}
 
-							@Override
-							public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
+								@Override
+								public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException {
+									return FileVisitResult.CONTINUE;
+								}
 
-							@Override
-							public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
+								@Override
+								public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException {
+									return FileVisitResult.CONTINUE;
+								}
 
-							@Override
-							public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
-								return FileVisitResult.CONTINUE;
-							}
-						});
+								@Override
+								public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
+									return FileVisitResult.CONTINUE;
+								}
+							});
+						}
 					}
 				}
+			} catch (IOException e) {
+				e.printStackTrace();
+				// Rethrow
 			}
-		} catch (IOException e) {
-			e.printStackTrace();
-			// Rethrow
+		}
+		if (moduleName == null) {
+			// Delegate to the boss, even if it means inaccurate error reporting at times
+			List<String> mods = JRTUtil.getModulesDeclaringPackage(this.file, qualifiedPackageName, moduleName);
+			return CharOperation.toCharArrays(mods);
 		}
 		return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
 	}
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
index 2c91339..74a2906 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -128,7 +128,6 @@
 		private PrintWriter log;
 		private Main main;
 		private PrintWriter out;
-		private HashMap<String, Object> parameters;
 		int tagBits;
 		private static final String CLASS = "class"; //$NON-NLS-1$
 		private static final String CLASS_FILE = "classfile"; //$NON-NLS-1$
@@ -220,7 +219,6 @@
 		public Logger(Main main, PrintWriter out, PrintWriter err) {
 			this.out = out;
 			this.err = err;
-			this.parameters = new HashMap<>();
 			this.main = main;
 		}
 
@@ -384,10 +382,11 @@
 					|| (unitSource == null)
 					|| ((length = unitSource.length) <= 0)
 					|| (endPosition > length)) {
-				this.parameters.put(Logger.VALUE, Messages.problem_noSourceInformation);
-				this.parameters.put(Logger.SOURCE_START, "-1"); //$NON-NLS-1$
-				this.parameters.put(Logger.SOURCE_END, "-1"); //$NON-NLS-1$
-				printTag(Logger.SOURCE_CONTEXT, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.VALUE, Messages.problem_noSourceInformation);
+				parameters.put(Logger.SOURCE_START, "-1"); //$NON-NLS-1$
+				parameters.put(Logger.SOURCE_END, "-1"); //$NON-NLS-1$
+				printTag(Logger.SOURCE_CONTEXT, parameters, true, true);
 				return;
 			}
 
@@ -413,11 +412,11 @@
 			// copy source
 			StringBuffer buffer = new StringBuffer();
 			buffer.append(unitSource, begin, end - begin + 1);
-
-			this.parameters.put(Logger.VALUE, String.valueOf(buffer));
-			this.parameters.put(Logger.SOURCE_START, Integer.toString(startPosition - begin));
-			this.parameters.put(Logger.SOURCE_END, Integer.toString(endPosition - begin));
-			printTag(Logger.SOURCE_CONTEXT, this.parameters, true, true);
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.VALUE, String.valueOf(buffer));
+			parameters.put(Logger.SOURCE_START, Integer.toString(startPosition - begin));
+			parameters.put(Logger.SOURCE_END, Integer.toString(endPosition - begin));
+			printTag(Logger.SOURCE_CONTEXT, parameters, true, true);
 		}
 		public void flush() {
 			this.out.flush();
@@ -511,8 +510,9 @@
 				}
 				File f = new File(fileName);
 				try {
-					this.parameters.put(Logger.PATH, f.getCanonicalPath());
-					printTag(Logger.CLASS_FILE, this.parameters, true, true);
+					HashMap<String, Object> parameters = new HashMap<>();
+					parameters.put(Logger.PATH, f.getCanonicalPath());
+					printTag(Logger.CLASS_FILE, parameters, true, true);
 				} catch (IOException e) {
 					logNoClassFileCreated(outputPath, relativeFileName, e);
 				}
@@ -524,10 +524,11 @@
 				final int length = classpaths.length;
 				if (length != 0) {
 					// generate xml output
-					printTag(Logger.CLASSPATHS, null, true, false);
+					printTag(Logger.CLASSPATHS, new HashMap<>(), true, false);
+					HashMap<String, Object> parameters = new HashMap<>();
 					for (int i = 0; i < length; i++) {
 						String classpath = classpaths[i].getPath();
-						this.parameters.put(Logger.PATH, classpath);
+						parameters.put(Logger.PATH, classpath);
 						File f = new File(classpath);
 						String id = null;
 						if (f.isFile()) {
@@ -544,8 +545,8 @@
 							id = Logger.CLASSPATH_FOLDER;
 						}
 						if (id != null) {
-							this.parameters.put(Logger.CLASSPATH_ID, id);
-							printTag(Logger.CLASSPATH, this.parameters, true, true);
+							parameters.put(Logger.CLASSPATH_ID, id);
+							printTag(Logger.CLASSPATH, parameters, true, true);
 						}
 					}
 					endTag(Logger.CLASSPATHS);
@@ -560,10 +561,11 @@
 				final int length = commandLineArguments.length;
 				if (length != 0) {
 					// generate xml output
-					printTag(Logger.COMMAND_LINE_ARGUMENTS, null, true, false);
+					printTag(Logger.COMMAND_LINE_ARGUMENTS, new HashMap<>(), true, false);
 					for (int i = 0; i < length; i++) {
-						this.parameters.put(Logger.VALUE, commandLineArguments[i]);
-						printTag(Logger.COMMAND_LINE_ARGUMENT, this.parameters, true, true);
+						HashMap<String, Object> parameters = new HashMap<>();
+						parameters.put(Logger.VALUE, commandLineArguments[i]);
+						printTag(Logger.COMMAND_LINE_ARGUMENT, parameters, true, true);
 					}
 					endTag(Logger.COMMAND_LINE_ARGUMENTS);
 				}
@@ -599,9 +601,10 @@
 					// ignore
 				}
 				message = buffer.toString();
-				this.parameters.put(Logger.MESSAGE, message);
-				this.parameters.put(Logger.CLASS, e.getClass());
-				printTag(Logger.EXCEPTION, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, message);
+				parameters.put(Logger.CLASS, e.getClass());
+				printTag(Logger.EXCEPTION, parameters, true, true);
 			}
 			String message = e.getMessage();
 			if (message == null) {
@@ -701,16 +704,18 @@
 
 		public void logUnavaibleAPT(String className) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, this.main.bind("configure.unavailableAPT", className)); //$NON-NLS-1$
-				printTag(Logger.ERROR_TAG, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, this.main.bind("configure.unavailableAPT", className)); //$NON-NLS-1$
+				printTag(Logger.ERROR_TAG, parameters, true, true);
 			}
 			this.printlnErr(this.main.bind("configure.unavailableAPT", className)); //$NON-NLS-1$
 		}
 
 		public void logIncorrectVMVersionForAnnotationProcessing() {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, this.main.bind("configure.incorrectVMVersionforAPT")); //$NON-NLS-1$
-				printTag(Logger.ERROR_TAG, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, this.main.bind("configure.incorrectVMVersionforAPT")); //$NON-NLS-1$
+				printTag(Logger.ERROR_TAG, parameters, true, true);
 			}
 			this.printlnErr(this.main.bind("configure.incorrectVMVersionforAPT")); //$NON-NLS-1$
 		}
@@ -720,13 +725,14 @@
 		 */
 		public void logNoClassFileCreated(String outputDir, String relativeFileName, IOException e) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, this.main.bind("output.noClassFileCreated", //$NON-NLS-1$
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, this.main.bind("output.noClassFileCreated", //$NON-NLS-1$
 					new String[] {
 						outputDir,
 						relativeFileName,
 						e.getMessage()
 					}));
-				printTag(Logger.ERROR_TAG, this.parameters, true, true);
+				printTag(Logger.ERROR_TAG, parameters, true, true);
 			}
 			this.printlnErr(this.main.bind("output.noClassFileCreated", //$NON-NLS-1$
 				new String[] {
@@ -741,8 +747,9 @@
 		 */
 		public void logNumberOfClassFilesGenerated(int exportedClassFilesCounter) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.VALUE, Integer.valueOf(exportedClassFilesCounter));
-				printTag(Logger.NUMBER_OF_CLASSFILES, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.VALUE, Integer.valueOf(exportedClassFilesCounter));
+				printTag(Logger.NUMBER_OF_CLASSFILES, parameters, true, true);
 			}
 			if (exportedClassFilesCounter == 1) {
 				printlnOut(this.main.bind("compile.oneClassFileGenerated")); //$NON-NLS-1$
@@ -757,7 +764,7 @@
 		 */
 		public void logOptions(Map<String, String> options) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				printTag(Logger.OPTIONS, null, true, false);
+				printTag(Logger.OPTIONS, new HashMap<>(), true, false);
 				final Set<Map.Entry<String, String>> entriesSet = options.entrySet();
 				Map.Entry<String, String>[] entries = entriesSet.toArray(new Map.Entry[entriesSet.size()]);
 				Arrays.sort(entries, new Comparator<Map.Entry<String, String>>() {
@@ -768,12 +775,13 @@
 						return entry1.getKey().compareTo(entry2.getKey());
 					}
 				});
+				HashMap<String, Object> parameters = new HashMap<>();
 				for (int i = 0, max = entries.length; i < max; i++) {
 					Map.Entry<String, String> entry = entries[i];
 					String key = entry.getKey();
-					this.parameters.put(Logger.KEY, key);
-					this.parameters.put(Logger.VALUE, entry.getValue());
-					printTag(Logger.OPTION, this.parameters, true, true);
+					parameters.put(Logger.KEY, key);
+					parameters.put(Logger.VALUE, entry.getValue());
+					printTag(Logger.OPTION, parameters, true, true);
 				}
 				endTag(Logger.OPTIONS);
 			}
@@ -784,8 +792,9 @@
 		 */
 		public void logPendingError(String error) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, error);
-				printTag(Logger.ERROR_TAG, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, error);
+				printTag(Logger.ERROR_TAG, parameters, true, true);
 			}
 			this.printlnErr(error);
 		}
@@ -795,8 +804,9 @@
 		 */
 		public void logWarning(String message) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, message);
-				printTag(Logger.WARNING_TAG, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, message);
+				printTag(Logger.WARNING_TAG, parameters, true, true);
 			}
 			this.printlnOut(message);
 		}
@@ -910,12 +920,13 @@
 			int globalErrorsCount, int globalWarningsCount, int globalInfoCount, int globalTasksCount) {
 			if ((this.tagBits & Logger.XML) != 0) {
 				// generate xml
-				this.parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(globalProblemsCount));
-				this.parameters.put(Logger.NUMBER_OF_ERRORS, Integer.valueOf(globalErrorsCount));
-				this.parameters.put(Logger.NUMBER_OF_WARNINGS, Integer.valueOf(globalWarningsCount));
-				this.parameters.put(Logger.NUMBER_OF_INFOS, Integer.valueOf(globalInfoCount));
-				this.parameters.put(Logger.NUMBER_OF_TASKS, Integer.valueOf(globalTasksCount));
-				printTag(Logger.PROBLEM_SUMMARY, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(globalProblemsCount));
+				parameters.put(Logger.NUMBER_OF_ERRORS, Integer.valueOf(globalErrorsCount));
+				parameters.put(Logger.NUMBER_OF_WARNINGS, Integer.valueOf(globalWarningsCount));
+				parameters.put(Logger.NUMBER_OF_INFOS, Integer.valueOf(globalInfoCount));
+				parameters.put(Logger.NUMBER_OF_TASKS, Integer.valueOf(globalTasksCount));
+				printTag(Logger.PROBLEM_SUMMARY, parameters, true, true);
 			}
 			if (globalProblemsCount == 1) {
 				String message = null;
@@ -1017,10 +1028,11 @@
 			long time = compilerStats.elapsedTime();
 			long lineCount = compilerStats.lineCount;
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.VALUE, Long.valueOf(time));
-				printTag(Logger.TIME, this.parameters, true, true);
-				this.parameters.put(Logger.VALUE, Long.valueOf(lineCount));
-				printTag(Logger.NUMBER_OF_LINES, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.VALUE, Long.valueOf(time));
+				printTag(Logger.TIME, parameters, true, true);
+				parameters.put(Logger.VALUE, Long.valueOf(lineCount));
+				printTag(Logger.NUMBER_OF_LINES, parameters, true, true);
 			}
 			if (lineCount != 0) {
 				printlnOut(
@@ -1096,8 +1108,9 @@
 		 */
 		public void logWrongJDK() {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.MESSAGE, this.main.bind("configure.requiresJDK1.2orAbove")); //$NON-NLS-1$
-				printTag(Logger.ERROR, this.parameters, true, true);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.MESSAGE, this.main.bind("configure.requiresJDK1.2orAbove")); //$NON-NLS-1$
+				printTag(Logger.ERROR, parameters, true, true);
 			}
 			this.printlnErr(this.main.bind("configure.requiresJDK1.2orAbove")); //$NON-NLS-1$
 		}
@@ -1106,13 +1119,14 @@
 			final int sourceStart = problem.getSourceStart();
 			final int sourceEnd = problem.getSourceEnd();
 			boolean isError = problem.isError();
-			this.parameters.put(Logger.PROBLEM_SEVERITY, isError ? Logger.ERROR : (problem.isInfo() ? Logger.INFO : Logger.WARNING));
-			this.parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
-			this.parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(sourceStart));
-			this.parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(sourceEnd));
-			printTag(Logger.EXTRA_PROBLEM_TAG, this.parameters, true, false);
-			this.parameters.put(Logger.VALUE, problem.getMessage());
-			printTag(Logger.PROBLEM_MESSAGE, this.parameters, true, true);
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.PROBLEM_SEVERITY, isError ? Logger.ERROR : (problem.isInfo() ? Logger.INFO : Logger.WARNING));
+			parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
+			parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(sourceStart));
+			parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(sourceEnd));
+			printTag(Logger.EXTRA_PROBLEM_TAG, parameters, true, false);
+			parameters.put(Logger.VALUE, problem.getMessage());
+			printTag(Logger.PROBLEM_MESSAGE, parameters, true, true);
 			extractContext(problem, null);
 			endTag(Logger.EXTRA_PROBLEM_TAG);
 		}
@@ -1126,31 +1140,34 @@
 			final int sourceStart = problem.getSourceStart();
 			final int sourceEnd = problem.getSourceEnd();
 			final int id = problem.getID();
-			this.parameters.put(Logger.ID, getFieldName(id)); // ID as field name
-			this.parameters.put(Logger.PROBLEM_ID, Integer.valueOf(id)); // ID as numeric value
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.ID, getFieldName(id)); // ID as field name
+			parameters.put(Logger.PROBLEM_ID, Integer.valueOf(id)); // ID as numeric value
 			boolean isError = problem.isError();
 			int severity = isError ? ProblemSeverities.Error : ProblemSeverities.Warning;
-			this.parameters.put(Logger.PROBLEM_SEVERITY, isError ? Logger.ERROR : (problem.isInfo() ? Logger.INFO : Logger.WARNING));
-			this.parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
-			this.parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(sourceStart));
-			this.parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(sourceEnd));
+			parameters.put(Logger.PROBLEM_SEVERITY, isError ? Logger.ERROR : (problem.isInfo() ? Logger.INFO : Logger.WARNING));
+			parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
+			parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(sourceStart));
+			parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(sourceEnd));
 			String problemOptionKey = getProblemOptionKey(id);
 			if (problemOptionKey != null) {
-				this.parameters.put(Logger.PROBLEM_OPTION_KEY, problemOptionKey);
+				parameters.put(Logger.PROBLEM_OPTION_KEY, problemOptionKey);
 			}
 			int categoryID = ProblemReporter.getProblemCategory(severity, id);
-			this.parameters.put(Logger.PROBLEM_CATEGORY_ID, Integer.valueOf(categoryID));
-			printTag(Logger.PROBLEM_TAG, this.parameters, true, false);
-			this.parameters.put(Logger.VALUE, problem.getMessage());
-			printTag(Logger.PROBLEM_MESSAGE, this.parameters, true, true);
+			parameters.put(Logger.PROBLEM_CATEGORY_ID, Integer.valueOf(categoryID));
+			printTag(Logger.PROBLEM_TAG, parameters, true, false);
+			parameters.put(Logger.VALUE, problem.getMessage());
+			printTag(Logger.PROBLEM_MESSAGE, parameters, true, true);
 			extractContext(problem, unitSource);
 			String[] arguments = problem.getArguments();
 			final int length = arguments.length;
 			if (length != 0) {
-				printTag(Logger.PROBLEM_ARGUMENTS, null, true, false);
+				parameters = new HashMap<>();
+				printTag(Logger.PROBLEM_ARGUMENTS, parameters, true, false);
 				for (int i = 0; i < length; i++) {
-					this.parameters.put(Logger.PROBLEM_ARGUMENT_VALUE, arguments[i]);
-					printTag(Logger.PROBLEM_ARGUMENT, this.parameters, true, true);
+					parameters = new HashMap<>();
+					parameters.put(Logger.PROBLEM_ARGUMENT_VALUE, arguments[i]);
+					printTag(Logger.PROBLEM_ARGUMENT, parameters, true, true);
 				}
 				endTag(Logger.PROBLEM_ARGUMENTS);
 			}
@@ -1163,16 +1180,17 @@
 		 *            the given unit source
 		 */
 		private void logXmlTask(CategorizedProblem problem, char[] unitSource) {
-			this.parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
-			this.parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(problem.getSourceStart()));
-			this.parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(problem.getSourceEnd()));
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.PROBLEM_LINE, Integer.valueOf(problem.getSourceLineNumber()));
+			parameters.put(Logger.PROBLEM_SOURCE_START, Integer.valueOf(problem.getSourceStart()));
+			parameters.put(Logger.PROBLEM_SOURCE_END, Integer.valueOf(problem.getSourceEnd()));
 			String problemOptionKey = getProblemOptionKey(problem.getID());
 			if (problemOptionKey != null) {
-				this.parameters.put(Logger.PROBLEM_OPTION_KEY, problemOptionKey);
+				parameters.put(Logger.PROBLEM_OPTION_KEY, problemOptionKey);
 			}
-			printTag(Logger.TASK, this.parameters, true, false);
-			this.parameters.put(Logger.VALUE, problem.getMessage());
-			printTag(Logger.PROBLEM_MESSAGE, this.parameters, true, true);
+			printTag(Logger.TASK, parameters, true, false);
+			parameters.put(Logger.VALUE, problem.getMessage());
+			printTag(Logger.PROBLEM_MESSAGE, parameters, true, true);
 			extractContext(problem, unitSource);
 			endTag(Logger.TASK);
 		}
@@ -1219,7 +1237,7 @@
 		public void printStats() {
 			final boolean isTimed = (this.main.timing & TIMING_ENABLED) != 0;
 			if ((this.tagBits & Logger.XML) != 0) {
-				printTag(Logger.STATS, null, true, false);
+				printTag(Logger.STATS, new HashMap<>(), true, false);
 			}
 			if (isTimed) {
 				CompilerStats compilerStats = this.main.batchCompiler.stats;
@@ -1242,9 +1260,9 @@
 
 		private void printTag(String name, HashMap<String, Object> params, boolean insertNewLine, boolean closeTag) {
 			if (this.log != null) {
-				((GenericXMLWriter) this.log).printTag(name, this.parameters, true, insertNewLine, closeTag);
+				((GenericXMLWriter) this.log).printTag(name, params, true, insertNewLine, closeTag);
 			}
-			this.parameters.clear();
+			if (params != null) params.clear();
 		}
 
 		public void setEmacs() {
@@ -1262,10 +1280,11 @@
 						// insert time stamp as comment
 						this.log.println("<!-- " + dateFormat.format(date) + " -->");//$NON-NLS-1$//$NON-NLS-2$
 						this.log.println(Logger.XML_DTD_DECLARATION);
-						this.parameters.put(Logger.COMPILER_NAME, this.main.bind("compiler.name")); //$NON-NLS-1$
-						this.parameters.put(Logger.COMPILER_VERSION, this.main.bind("compiler.version")); //$NON-NLS-1$
-						this.parameters.put(Logger.COMPILER_COPYRIGHT, this.main.bind("compiler.copyright")); //$NON-NLS-1$
-						printTag(Logger.COMPILER, this.parameters, true, false);
+						HashMap<String, Object> parameters = new HashMap<>();
+						parameters.put(Logger.COMPILER_NAME, this.main.bind("compiler.name")); //$NON-NLS-1$
+						parameters.put(Logger.COMPILER_VERSION, this.main.bind("compiler.version")); //$NON-NLS-1$
+						parameters.put(Logger.COMPILER_COPYRIGHT, this.main.bind("compiler.copyright")); //$NON-NLS-1$
+						printTag(Logger.COMPILER, parameters, true, false);
 					} else {
 						this.log = new PrintWriter(new FileOutputStream(logFileName, false));
 						this.log.println("# " + dateFormat.format(date));//$NON-NLS-1$
@@ -1281,8 +1300,9 @@
 			}
 		}
 		private void startLoggingExtraProblems(int count) {
-			this.parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(count));
-			printTag(Logger.EXTRA_PROBLEMS, this.parameters, true, false);
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(count));
+			printTag(Logger.EXTRA_PROBLEMS, parameters, true, false);
 		}
 
 		/**
@@ -1290,25 +1310,27 @@
 		 * Only use in xml mode.
 		 */
 		private void startLoggingProblems(int errors, int warnings, int infos) {
-			this.parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(errors + warnings));
-			this.parameters.put(Logger.NUMBER_OF_ERRORS, Integer.valueOf(errors));
-			this.parameters.put(Logger.NUMBER_OF_WARNINGS, Integer.valueOf(warnings));
-			this.parameters.put(Logger.NUMBER_OF_INFOS, Integer.valueOf(infos));
-			printTag(Logger.PROBLEMS, this.parameters, true, false);
+			HashMap<String, Object> parameters = new HashMap<>();
+			parameters.put(Logger.NUMBER_OF_PROBLEMS, Integer.valueOf(errors + warnings));
+			parameters.put(Logger.NUMBER_OF_ERRORS, Integer.valueOf(errors));
+			parameters.put(Logger.NUMBER_OF_WARNINGS, Integer.valueOf(warnings));
+			parameters.put(Logger.NUMBER_OF_INFOS, Integer.valueOf(infos));
+			printTag(Logger.PROBLEMS, parameters, true, false);
 		}
 
 		public void startLoggingSource(CompilationResult compilationResult) {
 			if ((this.tagBits & Logger.XML) != 0) {
 				ICompilationUnit compilationUnit = compilationResult.compilationUnit;
+				HashMap<String, Object> parameters = new HashMap<>();
 				if (compilationUnit != null) {
     				char[] fileName = compilationUnit.getFileName();
     				File f = new File(new String(fileName));
     				if (fileName != null) {
-    					this.parameters.put(Logger.PATH, f.getAbsolutePath());
+    					parameters.put(Logger.PATH, f.getAbsolutePath());
     				}
     				char[][] packageName = compilationResult.packageName;
     				if (packageName != null) {
-    					this.parameters.put(
+    					parameters.put(
     							Logger.PACKAGE,
     							new String(CharOperation.concatWith(packageName, File.separatorChar)));
     				}
@@ -1319,26 +1341,27 @@
 					}
 					if (destinationPath != null && destinationPath != NONE) {
 						if (File.separatorChar == '/') {
-							this.parameters.put(Logger.OUTPUT, destinationPath);
+							parameters.put(Logger.OUTPUT, destinationPath);
 						} else {
-							this.parameters.put(Logger.OUTPUT, destinationPath.replace('/', File.separatorChar));
+							parameters.put(Logger.OUTPUT, destinationPath.replace('/', File.separatorChar));
 						}
 					}
 				}
-				printTag(Logger.SOURCE, this.parameters, true, false);
+				printTag(Logger.SOURCE, parameters, true, false);
 			}
 		}
 
 		public void startLoggingSources() {
 			if ((this.tagBits & Logger.XML) != 0) {
-				printTag(Logger.SOURCES, null, true, false);
+				printTag(Logger.SOURCES, new HashMap<>(), true, false);
 			}
 		}
 
 		public void startLoggingTasks(int tasks) {
 			if ((this.tagBits & Logger.XML) != 0) {
-				this.parameters.put(Logger.NUMBER_OF_TASKS, Integer.valueOf(tasks));
-				printTag(Logger.TASKS, this.parameters, true, false);
+				HashMap<String, Object> parameters = new HashMap<>();
+				parameters.put(Logger.NUMBER_OF_TASKS, Integer.valueOf(tasks));
+				printTag(Logger.TASKS, parameters, true, false);
 			}
 		}
 	}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index 1f4172c..38e4acc 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1012,6 +1012,8 @@
 	int IllegalUseOfUnderscoreAsAnIdentifier = Syntax + Internal + 443;
 	 /** @since 3.10 */
 	int UninternedIdentityComparison = Syntax + Internal + 444;
+	 /** @since 3.24 */
+	int ErrorUseOfUnderscoreAsAnIdentifier = Syntax + Internal + 445;
 
 	// detected task
 	/** @since 2.1 */
@@ -2359,9 +2361,14 @@
 	 * @noreference preview feature error */
 	int RecordIllegalModifierForLocalRecord = PreviewRelated + 1762;
 
+	/* records - end */
+	/* Local and Nested Static Declarations - Begin */
 	/** @since 3.24
 	 * @noreference preview feature error */
 	int LocalStaticsIllegalVisibilityModifierForInterfaceLocalType = PreviewRelated + 1765;
+	/** @since 3.24
+	 * @noreference preview feature error */
+	int IllegalModifierForLocalEnumDeclaration = PreviewRelated + 1766;
 	/* records - end */
 
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
index 38fa4fc..0344823 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
@@ -65,6 +65,7 @@
 	public TypeReference type;
 	public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t;
 	public TypeBinding instanceofType; // set by InstanceofExpression to ensure we don't flag a necessary cast unnecessary
+	public boolean isVarTypeDeclaration; // set by LocalDeclaration to indicate we are initializing a var type declaration
 
 //expression.implicitConversion holds the cast for baseType casting
 public CastExpression(Expression expression, TypeReference type) {
@@ -646,6 +647,9 @@
 					&& expressionType.isProvablyDistinct(this.instanceofType)) {
 				this.bits |= ASTNode.DisableUnnecessaryCastCheck;
 			}
+			if (this.isVarTypeDeclaration && TypeBinding.notEquals(expressionType, castType)) {
+				this.bits |= ASTNode.DisableUnnecessaryCastCheck;
+			}
 			boolean isLegal = checkCastTypesCompatibility(scope, castType, expressionType, this.expression, true);
 			if (isLegal) {
 				this.expression.computeConversion(scope, castType, expressionType);
@@ -731,6 +735,10 @@
 	this.instanceofType = instanceofTypeBinding;
 }
 
+public void setVarTypeDeclaration(boolean value) {
+	this.isVarTypeDeclaration = value;
+}
+
 @Override
 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
 	if (visitor.visit(this, blockScope)) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
index 0c85305..d5687d0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
@@ -310,7 +310,8 @@
 			if (methodDeclaration == null
 					|| !methodDeclaration.isConstructor()
 					|| ((ConstructorDeclaration) methodDeclaration).constructorCall != this) {
-				scope.problemReporter().invalidExplicitConstructorCall(this);
+				if (!(methodDeclaration instanceof CompactConstructorDeclaration)) // already flagged for CCD
+						scope.problemReporter().invalidExplicitConstructorCall(this);
 				// fault-tolerance
 				if (this.qualification != null) {
 					this.qualification.resolveType(scope);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
index 6497127..db10305 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
@@ -1206,7 +1206,10 @@
 		if (field.type.isRawType()) {
 			if (referenceContext instanceof AbstractMethodDeclaration) {
 				AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration) referenceContext;
-				if (TypeBinding.notEquals(field.declaringClass, methodDecl.binding.declaringClass)) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=337962
+				ReferenceBinding declaringClass = methodDecl.binding != null
+						? methodDecl.binding.declaringClass
+						: methodDecl.scope.enclosingReceiverType();
+				if (TypeBinding.notEquals(field.declaringClass, declaringClass)) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=337962
 					return true;
 				}
 			} else if (referenceContext instanceof TypeDeclaration) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
index ff213b9..9dcd98b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
@@ -82,6 +82,8 @@
 	private static final int REPORTED_DEFINITIVE_LEAK = 64;
 	// a local declarations that acts as the element variable of a foreach loop (should never suggest to use t-w-r):
 	private static final int FOREACH_ELEMENT_VAR = 128;
+	// - passed as an effectively final resource in t-w-r JDK 9 and above
+	private static final int TWR_EFFECTIVELY_FINAL = 256;
 	public MessageSend acquisition;
 
 	public static boolean TEST_372319 = false; // see https://bugs.eclipse.org/372319
@@ -871,6 +873,10 @@
 		this.globalClosingState |= CLOSED_IN_NESTED_METHOD;
 	}
 
+	/** Mark that this resource is closed from a try-with-resource with the tracking variable being effectively final). */
+	public void markClosedEffectivelyFinal() {
+		this.globalClosingState |= TWR_EFFECTIVELY_FINAL;
+	}
 	/**
 	 * Mark that this resource is passed to some outside code
 	 * (as argument to a method/ctor call or as a return value from the current method),
@@ -1143,7 +1149,7 @@
 	}
 
 	public void reportExplicitClosing(ProblemReporter problemReporter) {
-		if ((this.globalClosingState & (OWNED_BY_OUTSIDE|REPORTED_EXPLICIT_CLOSE|FOREACH_ELEMENT_VAR)) == 0) { // can't use t-w-r for OWNED_BY_OUTSIDE
+		if ((this.globalClosingState & (TWR_EFFECTIVELY_FINAL|OWNED_BY_OUTSIDE|REPORTED_EXPLICIT_CLOSE|FOREACH_ELEMENT_VAR)) == 0) { // can't use t-w-r for OWNED_BY_OUTSIDE
 			this.globalClosingState |= REPORTED_EXPLICIT_CLOSE;
 			problemReporter.explicitlyClosedAutoCloseable(this);
 		}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
index 0b6454b..64c1c9a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
@@ -340,6 +340,9 @@
 
 		if (variableType == null) {
 			if (this.initialization != null) {
+				if (this.initialization instanceof CastExpression) {
+					((CastExpression)this.initialization).setVarTypeDeclaration(true);
+				}
 				this.initialization.resolveType(scope); // want to report all possible errors
 				if (isTypeNameVar && this.initialization.resolvedType != null) {
 					if (TypeBinding.equalsEquals(TypeBinding.NULL, this.initialization.resolvedType)) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
index bf9d869..b1bf2cd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
@@ -928,7 +928,7 @@
 
 @Override
 public Constant optimizedBooleanConstant() {
-	if (this.binding.isValidBinding()) {
+	if (this.binding.isValidBinding() && this.resolvedType != null) {
 		switch (this.resolvedType.id) {
 			case T_boolean :
 			case T_JavaLangBoolean :
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java
index 1bd8198..36843c4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java
@@ -157,19 +157,20 @@
 			if (resource instanceof LocalDeclaration) {
 				localVariableBinding = ((LocalDeclaration) resource).binding;
 				resolvedType = localVariableBinding.type;
-			} else { //expression
-				if (resource instanceof NameReference && ((NameReference) resource).binding instanceof LocalVariableBinding) {
-					localVariableBinding = (LocalVariableBinding) ((NameReference) resource).binding;
-				}
-				resolvedType = ((Expression) resource).resolvedType;
-			}
-			if (localVariableBinding != null) {
-				localVariableBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways.
 				if (localVariableBinding.closeTracker != null) {
 					// this was false alarm, we don't need to track the resource
 					localVariableBinding.closeTracker.withdraw();
 					localVariableBinding.closeTracker = null;
 				}
+			} else { //expression
+				if (resource instanceof NameReference && ((NameReference) resource).binding instanceof LocalVariableBinding) {
+					localVariableBinding = (LocalVariableBinding) ((NameReference) resource).binding;
+				}
+				resolvedType = ((Expression) resource).resolvedType;
+				recordCallingClose(currentScope, flowContext, tryInfo, (Expression)resource);
+			}
+			if (localVariableBinding != null) {
+				localVariableBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways.
 			}
 			MethodBinding closeMethod = findCloseMethod(resource, resolvedType);
 			if (closeMethod != null && closeMethod.isValidBinding() && closeMethod.returnType.id == TypeIds.T_void) {
@@ -284,19 +285,20 @@
 			if (resource instanceof LocalDeclaration) {
 				localVariableBinding = ((LocalDeclaration) this.resources[i]).binding;
 				resolvedType = localVariableBinding.type;
-			} else { // Expression
-				if (resource instanceof NameReference && ((NameReference) resource).binding instanceof LocalVariableBinding) {
-					localVariableBinding = (LocalVariableBinding)((NameReference) resource).binding;
-				}
-				resolvedType = ((Expression) resource).resolvedType;
-			}
-			if (localVariableBinding != null) {
-				localVariableBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways.
 				if (localVariableBinding.closeTracker != null) {
 					// this was false alarm, we don't need to track the resource
 					localVariableBinding.closeTracker.withdraw();
 					// keep the tracking variable in the resourceBinding in order to prevent creating a new one while analyzing the try block
 				}
+			} else { // Expression
+				if (resource instanceof NameReference && ((NameReference) resource).binding instanceof LocalVariableBinding) {
+					localVariableBinding = (LocalVariableBinding)((NameReference) resource).binding;
+				}
+				recordCallingClose(currentScope, flowContext, tryInfo, (Expression)resource);
+				resolvedType = ((Expression) resource).resolvedType;
+			}
+			if (localVariableBinding != null) {
+				localVariableBinding.useFlag = LocalVariableBinding.USED; // Is implicitly used anyways.
 			}
 			MethodBinding closeMethod = findCloseMethod(resource, resolvedType);
 			if (closeMethod != null && closeMethod.isValidBinding() && closeMethod.returnType.id == TypeIds.T_void) {
@@ -375,6 +377,17 @@
 		}
 	}
 }
+private void recordCallingClose(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Expression closeTarget) {
+	FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(closeTarget, flowInfo, flowContext);
+	if (trackingVariable != null) { // null happens if target is not a local variable or not an AutoCloseable
+		if (trackingVariable.methodScope == currentScope.methodScope()) {
+			trackingVariable.markClose(flowInfo, flowContext);
+		} else {
+			trackingVariable.markClosedInNestedMethod();
+		}
+		trackingVariable.markClosedEffectivelyFinal();
+	}
+}
 private MethodBinding findCloseMethod(final ASTNode resource, TypeBinding type) {
 	MethodBinding closeMethod = null;
 	if (type != null && type.isValidBinding() && type instanceof ReferenceBinding) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
index af9e108..124ccb2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
@@ -1316,6 +1316,7 @@
 		boolean needSerialVersion =
 						this.scope.compilerOptions().getSeverity(CompilerOptions.MissingSerialVersion) != ProblemSeverities.Ignore
 						&& sourceType.isClass()
+						&& !sourceType.isRecord()
 						&& sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoExternalizable, false /*Externalizable is not a class*/) == null
 						&& sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable, false /*Serializable is not a class*/) != null;
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IUpdatableModule.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IUpdatableModule.java
index 82e3bd1..ed99f5c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IUpdatableModule.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IUpdatableModule.java
@@ -79,6 +79,11 @@
 			}
 			return hash;
 		}
+		@Override
+		public String toString() {
+			return "add-exports " + CharOperation.charToString(this.name) + "=" + CharOperation.charToString(CharOperation.concatWith(this.targets, ','));  //$NON-NLS-1$//$NON-NLS-2$
+		}
+
 	}
 
 	class AddReads implements Consumer<IUpdatableModule> {
@@ -113,6 +118,10 @@
 		public int hashCode() {
 			return CharOperation.hashCode(this.targetModule);
 		}
+		@Override
+		public String toString() {
+			return "add-read " + CharOperation.charToString(this.targetModule); //$NON-NLS-1$
+		}
 	}
 	/** Structure for update operations, sorted by {@link UpdateKind}. */
 	class UpdatesByKind {
@@ -132,6 +141,21 @@
 					throw new IllegalArgumentException("Unknown enum value "+kind); //$NON-NLS-1$
 			}
 		}
+		@Override
+		public String toString() {
+			StringBuilder result = new StringBuilder();
+			for (Consumer<IUpdatableModule> consumer : this.moduleUpdates) {
+				if(result.length() > 0)
+					result.append("\n"); //$NON-NLS-1$
+				result.append(consumer);
+			}
+			for (Consumer<IUpdatableModule> consumer : this.packageUpdates) {
+				if(result.length() > 0)
+					result.append("\n"); //$NON-NLS-1$
+				result.append(consumer);
+			}
+			return result.toString();
+		}
 	}
 
 	/** Answer the name of the module to update. */
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index 9923990..fafd05d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -562,6 +562,8 @@
 		int modifiers = sourceType.modifiers;
 		boolean isPreviewEnabled = compilerOptions().sourceLevel == ClassFileConstants.getLatestJDKLevel() &&
 				compilerOptions().enablePreviewFeatures;
+		boolean flagSealedNonModifiers = isPreviewEnabled &&
+				(modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed)) != 0;
 		if (sourceType.isRecord()) {
 			/* JLS 14 Records Sec 8.10 - A record declaration is implicitly final. */
 			modifiers |= ClassFileConstants.AccFinal;
@@ -595,8 +597,10 @@
 					sourceType.modifiers = 0;
 					return;
 				}
-				if ((modifiers & ClassFileConstants.AccStatic) != 0) {
-					problemReporter().recordIllegalStaticModifierForLocalClassOrInterface(sourceType);
+				final int UNEXPECTED_MODIFIERS =~(ClassFileConstants.AccEnum | ClassFileConstants.AccStrictfp);
+				if ((modifiers & ExtraCompilerModifiers.AccJustFlag & UNEXPECTED_MODIFIERS) != 0
+						|| flagSealedNonModifiers) {
+					problemReporter().illegalModifierForLocalEnumDeclaration(sourceType);
 					return;
 				}
 				modifiers |= ClassFileConstants.AccStatic;
@@ -686,7 +690,7 @@
 			} else 	if (isPreviewEnabled && sourceType.isLocalType()) {
 				final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccAbstract | ClassFileConstants.AccInterface
 						| ClassFileConstants.AccStrictfp | ClassFileConstants.AccAnnotation);
-				if ((realModifiers & UNEXPECTED_MODIFIERS) != 0)
+				if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || flagSealedNonModifiers)
 					problemReporter().localStaticsIllegalVisibilityModifierForInterfaceLocalType(sourceType);
 //				if ((modifiers & ClassFileConstants.AccStatic) != 0) {
 //					problemReporter().recordIllegalStaticModifierForLocalClassOrInterface(sourceType);
@@ -709,8 +713,6 @@
 			}
 			modifiers |= ClassFileConstants.AccAbstract;
 			} else if ((realModifiers & ClassFileConstants.AccEnum) != 0) {
-			boolean flagSealedNonModifiers = isPreviewEnabled &&
-					(modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed)) != 0;
 			// detect abnormal cases for enums
 			if (isMemberType) { // includes member types defined inside local types
 				final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum);
@@ -722,6 +724,8 @@
 //					realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag;
 				}
 			} else if (sourceType.isLocalType()) {
+//				if (flagSealedNonModifiers)
+//					problemReporter().illegalModifierForLocalEnum(sourceType);
 				// each enum constant is an anonymous local type and its modifiers were already checked as an enum constant field
 			} else {
 				final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum);
@@ -823,9 +827,6 @@
 				if ((realModifiers & UNEXPECTED_MODIFIERS) != 0)
 					problemReporter().illegalModifierForMemberClass(sourceType);
 			} else if (sourceType.isLocalType()) {
-				boolean flagSealedNonModifiers = compilerOptions().sourceLevel >= ClassFileConstants.JDK15 &&
-						compilerOptions().enablePreviewFeatures &&
-						(modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed)) != 0;
 				final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp);
 				if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || flagSealedNonModifiers)
 					problemReporter().illegalModifierForLocalClass(sourceType);
@@ -1219,8 +1220,12 @@
 				permSubTypes.add(type);
 			}
 		}
+		if (sourceType.isSealed() && sourceType.isLocalType()) {
+			// bug xxxx flag Error and return;
+		}
 		if (permSubTypes.size() == 0) {
-			problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext);
+			if (!sourceType.isLocalType()) // error flagged already
+				problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext);
 			return;
 		}
 		sourceType.setPermittedTypes(permSubTypes.toArray(new ReferenceBinding[0]));
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 58fcc42..867fcf9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -82,6 +82,7 @@
 import org.eclipse.jdt.internal.compiler.ast.RecordComponent;
 import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
 import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SuperReference;
 import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
@@ -1163,10 +1164,7 @@
 			ReferenceBinding superInterface = this.superInterfaces[i];
 			if (superInterface != null && !checkPermitsAndAdd(superInterface, typesInCU)) {
 				TypeReference superInterfaceRef = typeDecl.superInterfaces[i];
-				if (this.isClass())
-					this.scope.problemReporter().sealedSuperClassDoesNotPermit(this, superInterfaceRef, superInterface);
-				else if (this.isInterface())
-					this.scope.problemReporter().sealedSuperInterfaceDoesNotPermit(this, superInterfaceRef, superInterface);
+				this.scope.problemReporter().sealedSuperInterfaceDoesNotPermit(this, superInterfaceRef, superInterface);
 			}
 		}
 	}
@@ -2449,6 +2447,12 @@
 			return true;
 		}
 	};
+	if ( methodDecl instanceof CompactConstructorDeclaration) {
+		CompactConstructorDeclaration ccd = (CompactConstructorDeclaration) methodDecl;
+		if (ccd.constructorCall == null) { // local traverse - super not set yet.
+			ccd.constructorCall = SuperReference.implicitSuperConstructorCall();
+		}
+	}
 	methodDecl.traverse(visitor, this.scope);
 }
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
index 1dd1f4f..dc4bab3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
@@ -123,6 +123,8 @@
 	// JEP 360 Sealed
 	char[] PERMITS = "permits".toCharArray(); //$NON-NLS-1$
 	char[] SEALED = "sealed".toCharArray(); //$NON-NLS-1$
+	String KEYWORD_EXTENDS = "extends"; //$NON-NLS-1$
+	String IMPLEMENTS = "implements"; //$NON-NLS-1$
 
     // jsr308
 	char[] TYPE_USE_TARGET  = "TYPE_USE".toCharArray(); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
index 26dc831..937e392 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
@@ -1095,6 +1095,7 @@
 		int moduleRefTokenCount = 0;
 		boolean lookForModule = false;
 		boolean parsingJava15Plus = this.scanner != null ? this.scanner.sourceLevel >= ClassFileConstants.JDK15 : false;
+		boolean stop = false;
 		nextToken : for (int iToken = 0; ; iToken++) {
 			if (iToken == 0) {
 				lookForModule = false;
@@ -1102,6 +1103,9 @@
 			} else {
 				prevToken = curToken;
 			}
+			if (stop) {
+				break;
+			}
 			int token = readTokenSafely();
 			curToken= token;
 			switch (token) {
@@ -1193,6 +1197,9 @@
 						moduleRefTokenCount = (iToken+1) / 2;
 						consumeToken();
 						lookForModule = false;
+						if (!considerNextChar()) {
+							stop = true;
+						}
 						break;
 					} // else fall through
 					// Note: Add other cases before this case.
@@ -1621,6 +1628,15 @@
 		return c;
 	}
 
+	private boolean considerNextChar() {
+		boolean consider = true;
+		char ch = getChar();
+		if (ch == ' ' || (System.lineSeparator().indexOf(ch) == 0)) {
+			consider = false;
+		}
+		return consider;
+	}
+
 	/*
 	 * Read token only if previous was consumed
 	 */
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index 57044ac..8ffc3bf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -81,9 +81,11 @@
 import java.io.CharConversionException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.Map;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -231,6 +233,17 @@
 	private static String SEALED = "sealed"; //$NON-NLS-1$
 	private static String RESTRICTED_IDENTIFIER_PERMITS = "RestrictedIdentifierpermits"; //$NON-NLS-1$
 	private static String PERMITS = "permits"; //$NON-NLS-1$
+	private static String PREVIEW_KEYWORD_NON_SEALED = "non-sealed"; //$NON-NLS-1$
+
+	private static Map<String, String> permittedRestrictedKeyWordMap;
+
+	static {
+		permittedRestrictedKeyWordMap = new HashMap<>();
+		permittedRestrictedKeyWordMap.put(RECORD, RESTRICTED_IDENTIFIER_RECORD);
+		permittedRestrictedKeyWordMap.put(SEALED, RESTRICTED_IDENTIFIER_SEALED);
+		permittedRestrictedKeyWordMap.put(PERMITS, RESTRICTED_IDENTIFIER_PERMITS);
+		permittedRestrictedKeyWordMap.put(PREVIEW_KEYWORD_NON_SEALED, PREVIEW_KEYWORD_NON_SEALED);
+	}
 
 public ProblemReporter(IErrorHandlingPolicy policy, CompilerOptions options, IProblemFactory problemFactory) {
 	super(policy, options, problemFactory);
@@ -8256,7 +8269,7 @@
 	String errorTokenName,
 	String expectedToken) {
 	if (isIdentifier(currentKind)) {
-		return validateRestrictedKeywords(errorTokenSource, start, end, true);
+		return validateRestrictedKeywords(errorTokenSource, expectedToken, start, end, true);
 	}
 	return false;
 }
@@ -9535,13 +9548,23 @@
 }
 //Returns true if the problem is handled and reported (only errors considered and not warnings)
 public boolean validateRestrictedKeywords(char[] name, int start, int end, boolean reportSyntaxError) {
+	return validateRestrictedKeywords(name, null, start, end, reportSyntaxError);
+}
+private boolean validateRestrictedKeywords(char[] name, String expectedToken, int start, int end, boolean reportSyntaxError) {
 	boolean isPreviewEnabled = this.options.enablePreviewFeatures;
+	if (expectedToken != null) {
+		String tokenName= new String(name);
+		String restrictedIdentifier= permittedRestrictedKeyWordMap.get(tokenName);
+		if (restrictedIdentifier == null || !restrictedIdentifier.equals(expectedToken)) {
+			return false;
+		}
+	}
 	for (JavaFeature feature : JavaFeature.values()) {
 		char[][] restrictedKeywords = feature.getRestrictedKeywords();
 		for (char[] k : restrictedKeywords) {
 			if (CharOperation.equals(name, k)) {
 				if (reportSyntaxError) {
-//					return validateJavaFeatureSupport(feature, start, end);
+					return validateJavaFeatureSupport(feature, start, end);
 				} else {
 					if (feature.isPreview()) {
 						int severity = isPreviewEnabled ? ProblemSeverities.Error | ProblemSeverities.Fatal : ProblemSeverities.Warning;
@@ -9606,9 +9629,10 @@
 }
 public void illegalUseOfUnderscoreAsAnIdentifier(int sourceStart, int sourceEnd, boolean reportError) {
 	this.underScoreIsError = reportError;
+	int problemId = (reportError) ? IProblem.ErrorUseOfUnderscoreAsAnIdentifier : IProblem.IllegalUseOfUnderscoreAsAnIdentifier;
 	try {
 		this.handle(
-			IProblem.IllegalUseOfUnderscoreAsAnIdentifier,
+			problemId,
 			NoArgument,
 			NoArgument,
 			sourceStart,
@@ -11908,6 +11932,15 @@
 		type.sourceStart(),
 		type.sourceEnd());
 }
+public void illegalModifierForLocalEnumDeclaration(SourceTypeBinding type) {
+	String[] arguments = new String[] {new String(type.sourceName())};
+	this.handle(
+		IProblem.IllegalModifierForLocalEnumDeclaration,
+		arguments,
+		arguments,
+		type.sourceStart(),
+		type.sourceEnd());
+}
 private void sealedMissingModifier(int problem, SourceTypeBinding type, TypeDeclaration typeDecl, TypeBinding superTypeBinding) {
 	if (!this.options.enablePreviewFeatures)
 		return;
@@ -11960,7 +11993,19 @@
 	sealedSuperTypeDoesNotPermit(IProblem.SealedSuperClassDoesNotPermit, type, superType, superTypeBinding);
 }
 public void sealedSuperInterfaceDoesNotPermit(SourceTypeBinding type, TypeReference superType, TypeBinding superTypeBinding) {
-	sealedSuperTypeDoesNotPermit(IProblem.SealedSuperInterfaceDoesNotPermit, type, superType, superTypeBinding);
+	if (!this.options.enablePreviewFeatures)
+		return;
+	String name = new String(type.sourceName());
+	String superTypeFullName = new String(superTypeBinding.readableName());
+	String superTypeShortName = new String(superTypeBinding.shortReadableName());
+	String keyword = type.isClass() ? new String(TypeConstants.IMPLEMENTS) : new String(TypeConstants.KEYWORD_EXTENDS);
+	if (superTypeShortName.equals(name)) superTypeShortName = superTypeFullName;
+	this.handle(
+			IProblem.SealedSuperInterfaceDoesNotPermit,
+		new String[] {name, superTypeFullName, keyword},
+		new String[] {name, superTypeShortName, keyword},
+		superType.sourceStart,
+		superType.sourceEnd);
 }
 
 public void sealedMissingSealedModifier(SourceTypeBinding type, ASTNode node) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index f2f39b3..94dd76f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -441,6 +441,7 @@
 442 = Enum constants cannot be surrounded by parenthesis
 443 = '_' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on
 444 = The uninterned types {0} and {1} should not be compared using ==/!= operators.
+445 = '_' is a keyword from source level 9 onwards, cannot be used as identifier
 
 450 = {0}{1}
 
@@ -1056,13 +1057,15 @@
 1758 = Illegal parameter name {0} in canonical constructor, expected {1}, the corresponding component name
 1759 = Illegal explicit assignment of a final field {0} in compact constructor
 1760 = A non-canonical constructor must start with an explicit invocation to a constructor
-1761 = A local interface, enum or record {0} is implicitly static; cannot have explicit static declaration
+1761 = A local class or interface {0} is implicitly static; cannot have explicit static declaration
 1762 = Illegal modifier for the local record {0}; only final and strictfp are permitted
 
 1765 = Illegal modifier for the local interface {0}; abstract and strictfp are the only modifiers allowed explicitly 
+1766 = Illegal modifier for local enum {0}; no explicit modifier is permitted
  
 1780 = The pattern variable {0} is not in scope in this location
 
+
 # Java 15 Preview - cont.
 
 # Additional doc
@@ -1083,14 +1086,14 @@
 1850 = The class {1} with a sealed direct superclass or a sealed direct superinterface {0} should be declared either final, sealed, or non-sealed
 1851 = A class {0} declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface
 1852 = The type {0} extending a sealed class {1} should be a permitted subtype of {1}
-1853 = The type {0} extending a sealed interface {1} should be a permitted subtype of {1}
+1853 = The type {0} that {2} a sealed interface {1} should be a permitted subtype of {1}
 1854 = A type declaration {0} that has a permits clause should have a sealed modifier
 1855 = The interface {1} with a sealed direct superinterface {0} should be declared either sealed or non-sealed
 1856 = Duplicate type {0} for the type {1} in the permits clause
 1857 = Permitted class {0} does not declare {1} as direct super class
 1858 = Permitted type {0} in a named module {1} should be declared in the same module {1} of declaring type {2}
 1859 = Permitted type {0} in an unnamed module should be declared in the same package {1} of declaring type {2}
-1860 = Sealed class lacks the permits clause and no top level or nested class from the same compilation unit declares {0} as its direct superclass
+1860 = Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares {0} as its direct superclass or superinterface
 1861 = An interface {0} is declared both sealed and non-sealed
 1862 = An interface {0} declared as non-sealed should have a sealed direct superinterface
 1863 = Permitted type {0} does not declare {1} as direct super interface 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CtSym.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CtSym.java
index d0dc2c9..7c38c6a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CtSym.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/CtSym.java
@@ -252,7 +252,7 @@
 			// Without this, org.eclipse.jdt.core.tests.model.ModuleBuilderTests.testConvertToModule() fails on 12+ JRE
 			path = releasePaths.get(moduleName + sep + qualifiedSignatureFileName);
 
-			// Special handling of broken module shema in java 11 for compilation with --release 10
+			// Special handling of broken module schema in java 11 for compilation with --release 10
 			if(path == null && !this.isJRE12Plus() && "A".equals(releaseCode)){ //$NON-NLS-1$
 				path = releasePaths.get(qualifiedSignatureFileName);
 			}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java
index 58e0d5b..981828d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java
@@ -48,7 +48,7 @@
 
 	public static final boolean DISABLE_CACHE = Boolean.getBoolean("org.eclipse.jdt.disable_JRT_cache"); //$NON-NLS-1$
 
-	public static final String JAVA_BASE = "java.base"; //$NON-NLS-1$
+	public static final String JAVA_BASE = "java.base".intern(); //$NON-NLS-1$
 	public static final char[] JAVA_BASE_CHAR = JAVA_BASE.toCharArray();
 	static final String MODULES_SUBDIR = "/modules"; //$NON-NLS-1$
 	static final String[] DEFAULT_MODULE = new String[]{JAVA_BASE};
@@ -160,7 +160,7 @@
 	 *  /modules/$MODULE/$PATH
 	 *  /packages/$PACKAGE/$MODULE
 	 *  The latter provides quick look up of the module that contains a particular package. However,
-	 *  this method only notifies its clients of the entries within the modules (latter) sub-directory.
+	 *  this method only notifies its clients of the entries within the modules (former) sub-directory.
 	 *  Clients can decide which notifications they want to receive. See {@link JRTUtil#NOTIFY_ALL},
 	 *  {@link JRTUtil#NOTIFY_FILES}, {@link JRTUtil#NOTIFY_PACKAGES} and {@link JRTUtil#NOTIFY_MODULES}.
 	 *
@@ -612,37 +612,44 @@
 		});
 	}
 
-	void cachePackage(String packageName, String module) {
-		packageName = packageName.intern();
-		module = module.intern();
+	synchronized void cachePackage(String packageName, String module) {
 		packageName = packageName.replace('.', '/');
-		Object current = this.packageToModule.get(packageName);
-		if (current == null) {
-			this.packageToModule.put(packageName, module);
-		} else if(current == module || current.equals(module)) {
+		String currentModule = this.packageToModule.get(packageName);
+		if (currentModule == null) {
+			// Nothing found? Cache and return
+			this.packageToModule.put(packageName.intern(), module.intern());
 			return;
-		} else if (current == JRTUtil.MULTIPLE) {
+		}
+		if(currentModule.equals(module)) {
+			// Same module found? Just return
+			return;
+		}
+
+		// We observe an additional module containing package
+		if (currentModule == JRTUtil.MULTIPLE) {
+			// We have already a list => update it
 			List<String> list = this.packageToModules.get(packageName);
 			if (!list.contains(module)) {
-				if (JRTUtil.JAVA_BASE == module || JRTUtil.JAVA_BASE.equals(module)) {
+				if (JRTUtil.JAVA_BASE.equals(module)) {
 					list.add(0, JRTUtil.JAVA_BASE);
 				} else {
-					list.add(module);
+					list.add(module.intern());
 				}
 			}
 		} else {
-			String first = (String) current;
-			this.packageToModule.put(packageName, JRTUtil.MULTIPLE);
+			// We found a second module => create a list
 			List<String> list = new ArrayList<String>();
 			// Just do this as comparator might be overkill
-			if (JRTUtil.JAVA_BASE == current || JRTUtil.JAVA_BASE.equals(current)) {
-				list.add(first);
-				list.add(module);
+			if (JRTUtil.JAVA_BASE == currentModule || JRTUtil.JAVA_BASE.equals(currentModule)) {
+				list.add(currentModule.intern());
+				list.add(module.intern());
 			} else {
-				list.add(module);
-				list.add(first);
+				list.add(module.intern());
+				list.add(currentModule.intern());
 			}
+			packageName = packageName.intern();
 			this.packageToModules.put(packageName, list);
+			this.packageToModule.put(packageName, JRTUtil.MULTIPLE);
 		}
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
index 430be8a..d4cc8ef 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
@@ -2225,7 +2225,7 @@
 		int startPosition = leftExpression.getStartPosition();
 		int sourceEnd = convertType.getStartPosition() + convertType.getLength() - 1;
 		if (DOMASTUtil.isInstanceofExpressionPatternSupported(this.ast) && expression.elementVariable != null) {
-			instanceOfExpression.setPatternVariable(convertToSingleVariableDeclaration(expression.elementVariable));
+			instanceOfExpression.setPatternVariable(convertToSimpleName(expression.elementVariable));
 			sourceEnd= expression.elementVariable.sourceEnd;
 		}
 		instanceOfExpression.setSourceRange(startPosition, sourceEnd - startPosition + 1);
@@ -3819,6 +3819,18 @@
 		return variableDecl;
 	}
 
+	protected SimpleName convertToSimpleName(LocalDeclaration localDeclaration) {
+		final SimpleName name = new SimpleName(this.ast);
+		name.internalSetIdentifier(new String(localDeclaration.name));
+		int start = localDeclaration.sourceStart;
+		int nameEnd = localDeclaration.sourceEnd;
+		name.setSourceRange(start, nameEnd - start + 1);
+		if (this.resolveBindings) {
+			recordNodes(name, localDeclaration);
+		}
+		return name;
+	}
+
 	private Dimension convertToDimensions(int start, int end, org.eclipse.jdt.internal.compiler.ast.Annotation[] annotation) {
 		int length = annotation == null ? 0 : annotation.length;
 		Dimension dimension = this.ast.newDimension();
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IMethodBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IMethodBinding.java
index f5f8f71..d6ed09e 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IMethodBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IMethodBinding.java
@@ -438,4 +438,18 @@
 	 */
 	public IVariableBinding[] getSyntheticOuterLocals();
 
+	/**
+	 * Returns if this is a compiler generated  equals(), hashCode(), toString() or any accessor
+	 * method of a Record or not.
+	 * Methods equals(), hashCode() and toString() and accessor methods of a Record do not have
+	 * AccSynthetic flag set for them even if they are compiler generated methods. To differentiate
+	 * between these above compiler generated methods and user created methods equals(), hashCode()
+	 * and toString() or accessor methods in a Record, this function can be used.
+	 *
+	 * @return <code>true</code> for compiler generated  equals(), hashCode() and toString() or any
+	 * accessor method of a Record, else it returns <code>false</code>.
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public boolean isSyntheticRecordMethod();
+
 }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/InstanceofExpression.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/InstanceofExpression.java
index 12ea6c7..9bd4ef8 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/InstanceofExpression.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/InstanceofExpression.java
@@ -47,12 +47,13 @@
 		new ChildPropertyDescriptor(InstanceofExpression.class, "rightOperand", Type.class, MANDATORY, CYCLE_RISK); //$NON-NLS-1$
 
 	/**
-	 * The "patternVariable" structural property of this node type (child type: {@link SingleVariableDeclaration}) (added in JLS14 API).
+	 * The "patternVariable" structural property of this node type (child type: {@link SimpleName}) (added in JLS14 API).
+	 * This contains the name of the instance variable.
 	 * @noreference This property is not intended to be referenced by clients as it is a part of Java preview feature.
 	 * @since 3.22
 	 */
 	public static final ChildPropertyDescriptor PATTERN_VARIABLE_PROPERTY =
-		new ChildPropertyDescriptor(InstanceofExpression.class, "patternVariable", SingleVariableDeclaration.class, OPTIONAL, NO_CYCLE_RISK); //$NON-NLS-1$
+		new ChildPropertyDescriptor(InstanceofExpression.class, "patternVariable", SimpleName.class, OPTIONAL, NO_CYCLE_RISK); //$NON-NLS-1$
 	/**
 	 * A list of property descriptors (element type:
 	 * {@link StructuralPropertyDescriptor}),
@@ -129,7 +130,7 @@
 	/**
 	 * The patternVariable declaration.
 	 */
-	private SingleVariableDeclaration patternVariable = null;
+	private SimpleName patternVariable = null;
 
 	/**
 	 * Creates a new AST node for an instanceof expression owned by the given
@@ -174,7 +175,7 @@
 			if (get) {
 				return getPatternVariable();
 			} else {
-				setPatternVariable((SingleVariableDeclaration) child);
+				setPatternVariable((SimpleName) child);
 				return null;
 			}
 		}
@@ -194,9 +195,9 @@
 		result.setLeftOperand((Expression) getLeftOperand().clone(target));
 		result.setRightOperand((Type) getRightOperand().clone(target));
 		if (DOMASTUtil.isInstanceofExpressionPatternSupported(target)) {
-			SingleVariableDeclaration pv = getPatternVariable();
+			SimpleName pv = getPatternVariable();
 			if (pv != null) {
-				result.setPatternVariable((SingleVariableDeclaration) pv.clone(target));
+				result.setPatternVariable((SimpleName) pv.clone(target));
 			}
 		}
 		return result;
@@ -311,7 +312,7 @@
 	 * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature.
 	 * @nooverride This method is not intended to be re-implemented or extended by clients as it is a part of Java preview feature.
 	 */
-	public SingleVariableDeclaration getPatternVariable() {
+	public SimpleName getPatternVariable() {
 		supportedOnlyIn15();
 		unsupportedWithoutPreviewError();
 		return this.patternVariable;
@@ -327,7 +328,7 @@
 	 * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature.
 	 * @nooverride This method is not intended to be re-implemented or extended by clients as it is a part of Java preview feature.
 	 */
-	public void setPatternVariable(SingleVariableDeclaration referencePatternVariable) {
+	public void setPatternVariable(SimpleName referencePatternVariable) {
 		supportedOnlyIn15();
 		unsupportedWithoutPreviewError();
 		if (referencePatternVariable == null) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/MethodBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/MethodBinding.java
index 7d938bf..585f650 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/MethodBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/MethodBinding.java
@@ -24,6 +24,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
@@ -607,4 +608,10 @@
 	public IVariableBinding[] getSyntheticOuterLocals() {
 		return NO_VARIABLE_BINDINGS;
 	}
+
+	@Override
+	public boolean isSyntheticRecordMethod() {
+		return ((getDeclaringClass().isRecord()) &&
+				(this.binding instanceof SyntheticMethodBinding));
+	}
 }
\ No newline at end of file
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
index 06575f5..adfd67b 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java
@@ -874,10 +874,10 @@
 	public boolean visit(InstanceofExpression node) {
 		node.getLeftOperand().accept(this);
 		this.buffer.append(" instanceof ");//$NON-NLS-1$
+		node.getRightOperand().accept(this);
 		if (DOMASTUtil.isInstanceofExpressionPatternSupported(node.getAST()) && node.getPatternVariable()!= null) {
+			this.buffer.append(" "); //$NON-NLS-1$
 			node.getPatternVariable().accept(this);
-		} else {
-			node.getRightOperand().accept(this);
 		}
 		return false;
 	}
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
index 7474baa..35eeaeb 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java
@@ -632,10 +632,10 @@
 	public boolean visit(InstanceofExpression node) {
 		getChildNode(node, InstanceofExpression.LEFT_OPERAND_PROPERTY).accept(this);
 		this.result.append(" instanceof "); //$NON-NLS-1$
+		getChildNode(node, InstanceofExpression.RIGHT_OPERAND_PROPERTY).accept(this);
 		if (DOMASTUtil.isInstanceofExpressionPatternSupported(node.getAST()) && node.getPatternVariable()!= null) {
+			this.result.append(" "); //$NON-NLS-1$
 			getChildNode(node, InstanceofExpression.PATTERN_VARIABLE_PROPERTY).accept(this);
-		} else {
-			getChildNode(node, InstanceofExpression.RIGHT_OPERAND_PROPERTY).accept(this);
 		}
 		return false;
 	}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JrtPackageFragmentRoot.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JrtPackageFragmentRoot.java
index 0028c34..2168da4 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JrtPackageFragmentRoot.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JrtPackageFragmentRoot.java
@@ -132,7 +132,11 @@
 	}
 	@Override
 	public int hashCode() {
-		return this.jarPath.hashCode() + this.moduleName.hashCode() + Arrays.hashCode(this.extraAttributes);
+		int hash = 31;
+		hash = Util.combineHashCodes(hash, this.jarPath.hashCode());
+		hash = Util.combineHashCodes(hash, this.moduleName.hashCode());
+		hash = Util.combineHashCodes(hash, Arrays.hashCode(this.extraAttributes));
+		return hash;
 	}
 	@Override
 	protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ModularClassFile.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ModularClassFile.java
index 14addc3..7751e9a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ModularClassFile.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ModularClassFile.java
@@ -36,8 +36,6 @@
  */
 public class ModularClassFile extends AbstractClassFile implements IModularClassFile {
 
-	private BinaryModule binaryModule;
-
 	protected ModularClassFile(PackageFragment parent) {
 		super(parent, TypeConstants.MODULE_INFO_NAME_STRING);
 	}
@@ -64,10 +62,7 @@
 		BinaryModule module = new BinaryModule(this, moduleInfo);
 		newElements.put(module, moduleInfo);
 		info.setChildren(new IJavaElement[] {module});
-		this.binaryModule = module;
-		if (info instanceof ClassFileInfo) {
-			((ClassFileInfo) info).setModule(module);
-		}
+		((ClassFileInfo) info).setModule(module);
 		((PackageFragmentRootInfo) getPackageFragmentRoot().getElementInfo()).setModule(module);
 		return true;
 	}
@@ -294,11 +289,10 @@
 
 	@Override
 	public IModuleDescription getModule() throws JavaModelException {
-		if (this.binaryModule == null) {
-			openWhenClosed(createElementInfo(), false, null);
-			if (this.binaryModule == null)
-				throw newNotPresentException();
+		BinaryModule module = (BinaryModule) ((ClassFileInfo) getElementInfo()).getModule();
+		if (module == null) {
+			throw newNotPresentException();
 		}
-		return this.binaryModule;
+		return module;
 	}
 }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
index 86feac3..5da4e51 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
@@ -77,6 +77,7 @@
 	private Map<IPackageFragmentRoot,IModuleDescription> rootToModule;
 
 	private long timeSpentInGetModulesDeclaringPackage;
+	private long timeSpentInFindTypes;
 
 	@Deprecated
 	public SearchableEnvironment(JavaProject project, org.eclipse.jdt.core.ICompilationUnit[] workingCopies) throws JavaModelException {
@@ -520,6 +521,10 @@
 	 * types are found relative to their enclosing type.
 	 */
 	public void findTypes(char[] prefix, final boolean findMembers, int matchRule, int searchFor, final ISearchRequestor storage, IProgressMonitor monitor) {
+		long start = -1;
+		if (NameLookup.VERBOSE)
+			start = System.currentTimeMillis();
+		
 		boolean camelCaseMatch = (matchRule & SearchPattern.R_CAMELCASE_MATCH) != 0;
 		/*
 			if (true){
@@ -676,6 +681,9 @@
 				new String(prefix),
 				storage,
 				convertSearchFilterToModelFilter(searchFor));
+		} finally {
+			if (NameLookup.VERBOSE)
+				this.timeSpentInFindTypes += System.currentTimeMillis()-start;
 		}
 	}
 
@@ -1178,7 +1186,8 @@
 			return;
 
 		Util.verbose(" TIME SPENT SearchableEnvironment");  //$NON-NLS-1$
-		Util.verbose(" -> getModulesDeclaringPackage: " +  this.timeSpentInGetModulesDeclaringPackage + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+		Util.verbose(" -> getModulesDeclaringPackage..." +  this.timeSpentInGetModulesDeclaringPackage + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+		Util.verbose(" -> findTypes...................." +  this.timeSpentInFindTypes + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
 
 		this.nameLookup.printTimeSpent();
 	}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
index 1976040..84fbc59 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
@@ -580,14 +580,14 @@
 	for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
 		if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
 		if (DEBUG) {
-			System.out.println("JavaBuilder: New location: " + newBinaryLocations[n] + "\n!= old location: " + oldBinaryLocations[o]); //$NON-NLS-1$ //$NON-NLS-2$
+			System.out.println("JavaBuilder: New test location: " + newBinaryLocations[n] + "\n!= old test location: " + oldBinaryLocations[o]); //$NON-NLS-1$ //$NON-NLS-2$
 			printLocations(newBinaryLocations, oldBinaryLocations);
 		}
 		return true;
 	}
 	if (n < newLength || o < oldLength) {
 		if (DEBUG) {
-			System.out.println("JavaBuilder: Number of binary folders/jar files has changed:"); //$NON-NLS-1$
+			System.out.println("JavaBuilder: Number of test binary folders/jar files has changed:"); //$NON-NLS-1$
 			printLocations(newBinaryLocations, oldBinaryLocations);
 		}
 		return true;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
index 4447a90..eb188d4 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java
@@ -59,8 +59,8 @@
 String javaProjectName;
 public ClasspathMultiDirectory[] sourceLocations;
 public ClasspathMultiDirectory[] testSourceLocations;
-ClasspathLocation[] binaryLocations;
-ClasspathLocation[] testBinaryLocations;
+public ClasspathLocation[] binaryLocations;
+public ClasspathLocation[] testBinaryLocations;
 // keyed by the project relative path of the type (i.e. "src1/p1/p2/A.java"), value is a ReferenceCollection or an AdditionalTypeCollection
 Map<String, ReferenceCollection> references;
 // keyed by qualified type name "p1/p2/A", value is the project relative path which defines this type "src1/p1/p2/A.java"
@@ -76,7 +76,7 @@
 private StringSet structurallyChangedTypes;
 public static int MaxStructurallyChangedTypes = 100; // keep track of ? structurally changed types, otherwise consider all to be changed
 
-public static final byte VERSION = 0x0022;
+public static final byte VERSION = 0x0023;
 
 static final byte SOURCE_FOLDER = 1;
 static final byte BINARY_FOLDER = 2;
@@ -256,129 +256,13 @@
 	newState.buildNumber = in.readInt();
 	newState.lastStructuralBuildTime = in.readLong();
 
-	int length = in.readInt();
-	newState.sourceLocations = new ClasspathMultiDirectory[length];
-	for (int i = 0; i < length; i++) {
-		IContainer sourceFolder = project, outputFolder = project;
-		String folderName;
-		if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
-		if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
-		ClasspathMultiDirectory md =
-			(ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in), readNames(in), in.readBoolean());
-		if (in.readBoolean())
-			md.hasIndependentOutputFolder = true;
-		newState.sourceLocations[i] = md;
-	}
+	newState.sourceLocations = readSourceLocations(project, in);
+	newState.binaryLocations = readBinaryLocations(project, in, newState.sourceLocations);
 
-	length = in.readInt();
-	newState.binaryLocations = new ClasspathLocation[length];
-	IWorkspaceRoot root = project.getWorkspace().getRoot();
-	for (int i = 0; i < length; i++) {
-		switch (in.readByte()) {
-			case SOURCE_FOLDER :
-				newState.binaryLocations[i] = newState.sourceLocations[in.readInt()];
-				break;
-			case BINARY_FOLDER :
-				IPath path = new Path(in.readUTF());
-				IContainer outputFolder = path.segmentCount() == 1
-					? (IContainer) root.getProject(path.toString())
-					: (IContainer) root.getFolder(path);
-					newState.binaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean(),
-							readRestriction(in), new Path(in.readUTF()), in.readBoolean());
-				break;
-			case EXTERNAL_JAR :
-				String jarPath = in.readUTF();
-				if (Util.isJrt(jarPath)) {
-					newState.binaryLocations[i] = ClasspathLocation.forJrtSystem(jarPath, readRestriction(in), new Path(in.readUTF()), in.readUTF());
-				} else {
-					newState.binaryLocations[i] = ClasspathLocation.forLibrary(jarPath, in.readLong(),
-							readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
-				}
-				break;
-			case INTERNAL_JAR :
-					newState.binaryLocations[i] = ClasspathLocation.forLibrary(root.getFile(new Path(in.readUTF())),
-							readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
-					break;
-		}
-		ClasspathLocation loc = newState.binaryLocations[i];
-		char[] patchName = readName(in);
-		loc.patchModuleName = patchName.length > 0 ? new String(patchName) : null;
-		int limitSize = in.readInt();
-		if (limitSize != 0) {
-			loc.limitModuleNames = new LinkedHashSet<>(limitSize);
-			for (int j = 0; j < limitSize; j++) {
-				loc.limitModuleNames.add(in.readUTF());
-			}
-		} else {
-			loc.limitModuleNames = null;
-		}
-		IUpdatableModule.UpdatesByKind updates = new IUpdatableModule.UpdatesByKind();
-		List<Consumer<IUpdatableModule>> packageUpdates = null;
-		int packageUpdatesSize = in.readInt();
-		if (packageUpdatesSize != 0) {
-			packageUpdates = updates.getList(UpdateKind.PACKAGE, true);
-			for (int j = 0; j < packageUpdatesSize; j++) {
-				char[] pkgName = readName(in);
-				char[][] targets = readNames(in);
-				packageUpdates.add(new AddExports(pkgName, targets));
-			}
-		}
-		List<Consumer<IUpdatableModule>> moduleUpdates = null;
-		int moduleUpdatesSize = in.readInt();
-		if (moduleUpdatesSize != 0) {
-			moduleUpdates = updates.getList(UpdateKind.MODULE, true);
-			char[] modName = readName(in);
-			moduleUpdates.add(new AddReads(modName));
-		}
-		if (packageUpdates != null || moduleUpdates != null)
-			loc.updates = updates;
-	}
+	newState.testSourceLocations = readSourceLocations(project, in);
+	newState.testBinaryLocations = readBinaryLocations(project, in, newState.testSourceLocations);
 
-	length = in.readInt();
-	newState.testSourceLocations = new ClasspathMultiDirectory[length];
-	for (int i = 0; i < length; i++) {
-		IContainer sourceFolder = project, outputFolder = project;
-		String folderName;
-		if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
-		if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
-		ClasspathMultiDirectory md =
-			(ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in), readNames(in), in.readBoolean());
-		if (in.readBoolean())
-			md.hasIndependentOutputFolder = true;
-		newState.testSourceLocations[i] = md;
-	}
-
-	length = in.readInt();
-	newState.testBinaryLocations = new ClasspathLocation[length];
-	for (int i = 0; i < length; i++) {
-		switch (in.readByte()) {
-			case SOURCE_FOLDER :
-				newState.testBinaryLocations[i] = newState.testSourceLocations[in.readInt()];
-				break;
-			case BINARY_FOLDER :
-				IPath path = new Path(in.readUTF());
-				IContainer outputFolder = path.segmentCount() == 1
-					? (IContainer) root.getProject(path.toString())
-					: (IContainer) root.getFolder(path);
-					newState.testBinaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean(),
-							readRestriction(in), new Path(in.readUTF()), in.readBoolean());
-				break;
-			case EXTERNAL_JAR :
-				String jarPath = in.readUTF();
-				if (Util.isJrt(jarPath)) {
-					newState.testBinaryLocations[i] = ClasspathLocation.forJrtSystem(jarPath, readRestriction(in), new Path(in.readUTF()), in.readUTF());
-				} else {
-						newState.testBinaryLocations[i] = ClasspathLocation.forLibrary(jarPath, in.readLong(),
-								readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
-				}
-				break;
-			case INTERNAL_JAR :
-					newState.testBinaryLocations[i] = ClasspathLocation.forLibrary(root.getFile(new Path(in.readUTF())),
-							readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
-					break;
-		}
-	}
-
+	int length;
 	newState.structuralBuildTimes = new SimpleLookupTable(length = in.readInt());
 	for (int i = 0; i < length; i++)
 		newState.structuralBuildTimes.put(in.readUTF(), Long.valueOf(in.readLong()));
@@ -445,6 +329,90 @@
 	return newState;
 }
 
+private static ClasspathMultiDirectory[] readSourceLocations(IProject project, DataInputStream in) throws IOException {
+	int length = in.readInt();
+	ClasspathMultiDirectory[] sourceLocations = new ClasspathMultiDirectory[length];
+	for (int i = 0; i < length; i++) {
+		IContainer sourceFolder = project, outputFolder = project;
+		String folderName;
+		if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
+		if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
+		ClasspathMultiDirectory md =
+			(ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in), readNames(in), in.readBoolean());
+		if (in.readBoolean())
+			md.hasIndependentOutputFolder = true;
+		sourceLocations[i] = md;
+	}
+	return sourceLocations;
+}
+
+private static ClasspathLocation[] readBinaryLocations(IProject project, DataInputStream in, ClasspathMultiDirectory[] sourceLocations) throws IOException, CoreException {
+	int length = in.readInt();
+	ClasspathLocation[] locations = new ClasspathLocation[length];
+	IWorkspaceRoot root = project.getWorkspace().getRoot();
+	for (int i = 0; i < length; i++) {
+		switch (in.readByte()) {
+			case SOURCE_FOLDER :
+				locations[i] = sourceLocations[in.readInt()];
+				break;
+			case BINARY_FOLDER :
+				IPath path = new Path(in.readUTF());
+				IContainer outputFolder = path.segmentCount() == 1
+					? (IContainer) root.getProject(path.toString())
+					: (IContainer) root.getFolder(path);
+					locations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean(),
+							readRestriction(in), new Path(in.readUTF()), in.readBoolean());
+				break;
+			case EXTERNAL_JAR :
+				String jarPath = in.readUTF();
+				if (Util.isJrt(jarPath)) {
+					locations[i] = ClasspathLocation.forJrtSystem(jarPath, readRestriction(in), new Path(in.readUTF()), in.readUTF());
+				} else {
+					locations[i] = ClasspathLocation.forLibrary(jarPath, in.readLong(),
+							readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
+				}
+				break;
+			case INTERNAL_JAR :
+					locations[i] = ClasspathLocation.forLibrary(root.getFile(new Path(in.readUTF())),
+							readRestriction(in), new Path(in.readUTF()), in.readBoolean(), in.readUTF());
+					break;
+		}
+		ClasspathLocation loc = locations[i];
+		char[] patchName = readName(in);
+		loc.patchModuleName = patchName.length > 0 ? new String(patchName) : null;
+		int limitSize = in.readInt();
+		if (limitSize != 0) {
+			loc.limitModuleNames = new LinkedHashSet<>(limitSize);
+			for (int j = 0; j < limitSize; j++) {
+				loc.limitModuleNames.add(in.readUTF());
+			}
+		} else {
+			loc.limitModuleNames = null;
+		}
+		IUpdatableModule.UpdatesByKind updates = new IUpdatableModule.UpdatesByKind();
+		List<Consumer<IUpdatableModule>> packageUpdates = null;
+		int packageUpdatesSize = in.readInt();
+		if (packageUpdatesSize != 0) {
+			packageUpdates = updates.getList(UpdateKind.PACKAGE, true);
+			for (int j = 0; j < packageUpdatesSize; j++) {
+				char[] pkgName = readName(in);
+				char[][] targets = readNames(in);
+				packageUpdates.add(new AddExports(pkgName, targets));
+			}
+		}
+		List<Consumer<IUpdatableModule>> moduleUpdates = null;
+		int moduleUpdatesSize = in.readInt();
+		if (moduleUpdatesSize != 0) {
+			moduleUpdates = updates.getList(UpdateKind.MODULE, true);
+			char[] modName = readName(in);
+			moduleUpdates.add(new AddReads(modName));
+		}
+		if (packageUpdates != null || moduleUpdates != null)
+			loc.updates = updates;
+	}
+	return locations;
+}
+
 private static char[] readName(DataInputStream in) throws IOException {
 	int nLength = in.readInt();
 	char[] name = new char[nLength];
@@ -526,184 +494,29 @@
  * ClasspathMultiDirectory[]
  * int			id
  * String		path(s)
-*/
-	out.writeInt(length = this.sourceLocations.length);
-	for (int i = 0; i < length; i++) {
-		ClasspathMultiDirectory md = this.sourceLocations[i];
-		out.writeUTF(md.sourceFolder.getProjectRelativePath().toString());
-		out.writeUTF(md.binaryFolder.getProjectRelativePath().toString());
-		writeNames(md.inclusionPatterns, out);
-		writeNames(md.exclusionPatterns, out);
-		out.writeBoolean(md.ignoreOptionalProblems);
-		out.writeBoolean(md.hasIndependentOutputFolder);
-	}
+ */
+	writeSourceLocations(out, this.sourceLocations);
 
 /*
  * ClasspathLocation[]
  * int			id
  * String		path(s)
-*/
-	out.writeInt(length = this.binaryLocations.length);
-	for (int i = 0; i < length; i++) {
-		ClasspathLocation c = this.binaryLocations[i];
-		if (c instanceof ClasspathMultiDirectory) {
-			out.writeByte(SOURCE_FOLDER);
-			for (int j = 0, m = this.sourceLocations.length; j < m; j++) {
-				if (this.sourceLocations[j] == c) {
-					out.writeInt(j);
-					//continue next;
-				}
-			}
-		} else if (c instanceof ClasspathDirectory) {
-			out.writeByte(BINARY_FOLDER);
-			ClasspathDirectory cd = (ClasspathDirectory) c;
-			out.writeUTF(cd.binaryFolder.getFullPath().toString());
-			out.writeBoolean(cd.isOutputFolder);
-			writeRestriction(cd.accessRuleSet, out);
-			out.writeUTF(cd.externalAnnotationPath != null ? cd.externalAnnotationPath : ""); //$NON-NLS-1$
-			out.writeBoolean(cd.isOnModulePath);
-		} else if (c instanceof ClasspathJar) {
-			ClasspathJar jar = (ClasspathJar) c;
-			if (jar.resource == null) {
-				out.writeByte(EXTERNAL_JAR);
-				out.writeUTF(jar.zipFilename);
-				out.writeLong(jar.lastModified());
-			} else {
-				out.writeByte(INTERNAL_JAR);
-				out.writeUTF(jar.resource.getFullPath().toString());
-			}
-			writeRestriction(jar.accessRuleSet, out);
-			out.writeUTF(jar.externalAnnotationPath != null ? jar.externalAnnotationPath : ""); //$NON-NLS-1$
-			out.writeBoolean(jar.isOnModulePath);
-			out.writeUTF(jar.compliance == null ? "" : jar.compliance); //$NON-NLS-1$
+ */
+	writeBinaryLocations(out, this.binaryLocations, this.sourceLocations);
 
-		} else if (c instanceof ClasspathJrt) {
-			ClasspathJrt jrt = (ClasspathJrt) c;
-			out.writeByte(EXTERNAL_JAR);
-			out.writeUTF(jrt.zipFilename);
-			writeRestriction(jrt.accessRuleSet, out);
-			out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$
-			if (jrt instanceof ClasspathJrtWithReleaseOption)
-				out.writeUTF(((ClasspathJrtWithReleaseOption) jrt).release);
-			else
-				out.writeUTF(""); //$NON-NLS-1$
-		}
-		char[] patchName = c.patchModuleName == null ? CharOperation.NO_CHAR : c.patchModuleName.toCharArray();
-		writeName(patchName, out);
-		if (c.limitModuleNames != null) {
-			out.writeInt(c.limitModuleNames.size());
-			for (String name : c.limitModuleNames) {
-				out.writeUTF(name);
-			}
-		} else {
-			out.writeInt(0);
-		}
-		if (c.updates != null) {
-			List<Consumer<IUpdatableModule>> pu = c.updates.getList(UpdateKind.PACKAGE, false);
-			if (pu != null) {
-				Map<String, List<Consumer<IUpdatableModule>>> map = pu.stream().
-						collect(Collectors.groupingBy(
-								update -> CharOperation.charToString(((IUpdatableModule.AddExports)update).getName())));
-				out.writeInt(map.size());
-				map.entrySet().stream().forEach(entry -> {
-					String pkgName = entry.getKey();
-					try {
-						writeName(pkgName.toCharArray(), out);
-						char[][] targetModules = entry.getValue().stream()
-								.map(consumer -> ((IUpdatableModule.AddExports) consumer).getTargetModules())
-								.filter(targets -> targets != null)
-								.reduce((f,s) -> CharOperation.arrayConcat(f,s))
-								.orElse(null);
-						writeNames(targetModules, out);
-					} catch (IOException e) {
-						// ignore
-					}
+/*
+ * ClasspathMultiDirectory[]
+ * int			id
+ * String		path(s)
+ */
+	writeSourceLocations(out, this.testSourceLocations);
 
-				});
-			} else {
-				out.writeInt(0);
-			}
-			List<Consumer<IUpdatableModule>> mu = c.updates.getList(UpdateKind.MODULE, false);
-			if (mu != null) {
-				out.writeInt(mu.size());
-				for (Consumer<IUpdatableModule> cons : mu) {
-					AddReads m = (AddReads) cons;
-					writeName(m.getTarget(), out);
-				}
-			} else {
-				out.writeInt(0);
-			}
-		} else {
-			out.writeInt(0);
-			out.writeInt(0);
-		}
-	}
-	/*
-	 * ClasspathMultiDirectory[]
-	 * int			id
-	 * String		path(s)
-	*/
-		out.writeInt(length = this.testSourceLocations.length);
-		for (int i = 0; i < length; i++) {
-			ClasspathMultiDirectory md = this.testSourceLocations[i];
-			out.writeUTF(md.sourceFolder.getProjectRelativePath().toString());
-			out.writeUTF(md.binaryFolder.getProjectRelativePath().toString());
-			writeNames(md.inclusionPatterns, out);
-			writeNames(md.exclusionPatterns, out);
-			out.writeBoolean(md.ignoreOptionalProblems);
-			out.writeBoolean(md.hasIndependentOutputFolder);
-		}
-
-	/*
-	 * ClasspathLocation[]
-	 * int			id
-	 * String		path(s)
-	*/
-		out.writeInt(length = this.testBinaryLocations.length);
-		next : for (int i = 0; i < length; i++) {
-			ClasspathLocation c = this.testBinaryLocations[i];
-			if (c instanceof ClasspathMultiDirectory) {
-				out.writeByte(SOURCE_FOLDER);
-				for (int j = 0, m = this.testSourceLocations.length; j < m; j++) {
-					if (this.testSourceLocations[j] == c) {
-						out.writeInt(j);
-						continue next;
-					}
-				}
-			} else if (c instanceof ClasspathDirectory) {
-				out.writeByte(BINARY_FOLDER);
-				ClasspathDirectory cd = (ClasspathDirectory) c;
-				out.writeUTF(cd.binaryFolder.getFullPath().toString());
-				out.writeBoolean(cd.isOutputFolder);
-				writeRestriction(cd.accessRuleSet, out);
-				out.writeUTF(cd.externalAnnotationPath != null ? cd.externalAnnotationPath : ""); //$NON-NLS-1$
-				out.writeBoolean(cd.isOnModulePath);
-			} else if (c instanceof ClasspathJar) {
-				ClasspathJar jar = (ClasspathJar) c;
-				if (jar.resource == null) {
-					out.writeByte(EXTERNAL_JAR);
-					out.writeUTF(jar.zipFilename);
-					out.writeLong(jar.lastModified());
-				} else {
-					out.writeByte(INTERNAL_JAR);
-					out.writeUTF(jar.resource.getFullPath().toString());
-				}
-				writeRestriction(jar.accessRuleSet, out);
-				out.writeUTF(jar.externalAnnotationPath != null ? jar.externalAnnotationPath : ""); //$NON-NLS-1$
-				out.writeBoolean(jar.isOnModulePath);
-				out.writeUTF(jar.compliance != null ? jar.compliance : ""); //$NON-NLS-1$
-			} else if (c instanceof ClasspathJrt) {
-				ClasspathJrt jrt = (ClasspathJrt) c;
-				out.writeByte(EXTERNAL_JAR);
-				out.writeUTF(jrt.zipFilename);
-				writeRestriction(jrt.accessRuleSet, out);
-				out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$
-				if (jrt instanceof ClasspathJrtWithReleaseOption)
-					out.writeUTF(((ClasspathJrtWithReleaseOption) jrt).release);
-				else
-					out.writeUTF(""); //$NON-NLS-1$
-			}
-		}
+/*
+ * ClasspathLocation[]
+ * int			id
+ * String		path(s)
+ */
+	writeBinaryLocations(out, this.testBinaryLocations, this.testSourceLocations);
 
 /*
  * Structural build numbers table
@@ -888,6 +701,126 @@
 	}
 }
 
+private void writeSourceLocations(DataOutputStream out, ClasspathMultiDirectory[] srcLocations) throws IOException {
+	int length;
+	out.writeInt(length = srcLocations.length);
+	for (int i = 0; i < length; i++) {
+		ClasspathMultiDirectory md = srcLocations[i];
+		out.writeUTF(md.sourceFolder.getProjectRelativePath().toString());
+		out.writeUTF(md.binaryFolder.getProjectRelativePath().toString());
+		writeNames(md.inclusionPatterns, out);
+		writeNames(md.exclusionPatterns, out);
+		out.writeBoolean(md.ignoreOptionalProblems);
+		out.writeBoolean(md.hasIndependentOutputFolder);
+	}
+}
+
+private void writeBinaryLocations(DataOutputStream out, ClasspathLocation[] locations, ClasspathMultiDirectory[] srcLocations)
+		throws IOException {
+	/*
+	 * ClasspathLocation[]
+	 * int			id
+	 * String		path(s)
+	 * String       module updates
+	*/
+
+	out.writeInt(locations.length);
+	for (int i = 0; i < locations.length; i++) {
+		ClasspathLocation c = locations[i];
+		if (c instanceof ClasspathMultiDirectory) {
+			out.writeByte(SOURCE_FOLDER);
+			for (int j = 0, m = srcLocations.length; j < m; j++) {
+				if (srcLocations[j] == c) {
+					out.writeInt(j);
+					//continue next;
+				}
+			}
+		} else if (c instanceof ClasspathDirectory) {
+			out.writeByte(BINARY_FOLDER);
+			ClasspathDirectory cd = (ClasspathDirectory) c;
+			out.writeUTF(cd.binaryFolder.getFullPath().toString());
+			out.writeBoolean(cd.isOutputFolder);
+			writeRestriction(cd.accessRuleSet, out);
+			out.writeUTF(cd.externalAnnotationPath != null ? cd.externalAnnotationPath : ""); //$NON-NLS-1$
+			out.writeBoolean(cd.isOnModulePath);
+		} else if (c instanceof ClasspathJar) {
+			ClasspathJar jar = (ClasspathJar) c;
+			if (jar.resource == null) {
+				out.writeByte(EXTERNAL_JAR);
+				out.writeUTF(jar.zipFilename);
+				out.writeLong(jar.lastModified());
+			} else {
+				out.writeByte(INTERNAL_JAR);
+				out.writeUTF(jar.resource.getFullPath().toString());
+			}
+			writeRestriction(jar.accessRuleSet, out);
+			out.writeUTF(jar.externalAnnotationPath != null ? jar.externalAnnotationPath : ""); //$NON-NLS-1$
+			out.writeBoolean(jar.isOnModulePath);
+			out.writeUTF(jar.compliance == null ? "" : jar.compliance); //$NON-NLS-1$
+
+		} else if (c instanceof ClasspathJrt) {
+			ClasspathJrt jrt = (ClasspathJrt) c;
+			out.writeByte(EXTERNAL_JAR);
+			out.writeUTF(jrt.zipFilename);
+			writeRestriction(jrt.accessRuleSet, out);
+			out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$
+			if (jrt instanceof ClasspathJrtWithReleaseOption)
+				out.writeUTF(((ClasspathJrtWithReleaseOption) jrt).release);
+			else
+				out.writeUTF(""); //$NON-NLS-1$
+		}
+		char[] patchName = c.patchModuleName == null ? CharOperation.NO_CHAR : c.patchModuleName.toCharArray();
+		writeName(patchName, out);
+		if (c.limitModuleNames != null) {
+			out.writeInt(c.limitModuleNames.size());
+			for (String name : c.limitModuleNames) {
+				out.writeUTF(name);
+			}
+		} else {
+			out.writeInt(0);
+		}
+		if (c.updates != null) {
+			List<Consumer<IUpdatableModule>> pu = c.updates.getList(UpdateKind.PACKAGE, false);
+			if (pu != null) {
+				Map<String, List<Consumer<IUpdatableModule>>> map = pu.stream().
+						collect(Collectors.groupingBy(
+								update -> CharOperation.charToString(((IUpdatableModule.AddExports)update).getName())));
+				out.writeInt(map.size());
+				map.entrySet().stream().forEach(entry -> {
+					String pkgName = entry.getKey();
+					try {
+						writeName(pkgName.toCharArray(), out);
+						char[][] targetModules = entry.getValue().stream()
+								.map(consumer -> ((IUpdatableModule.AddExports) consumer).getTargetModules())
+								.filter(targets -> targets != null)
+								.reduce((f,s) -> CharOperation.arrayConcat(f,s))
+								.orElse(null);
+						writeNames(targetModules, out);
+					} catch (IOException e) {
+						// ignore
+					}
+
+				});
+			} else {
+				out.writeInt(0);
+			}
+			List<Consumer<IUpdatableModule>> mu = c.updates.getList(UpdateKind.MODULE, false);
+			if (mu != null) {
+				out.writeInt(mu.size());
+				for (Consumer<IUpdatableModule> cons : mu) {
+					AddReads m = (AddReads) cons;
+					writeName(m.getTarget(), out);
+				}
+			} else {
+				out.writeInt(0);
+			}
+		} else {
+			out.writeInt(0);
+			out.writeInt(0);
+		}
+	}
+}
+
 private void writeName(char[] name, DataOutputStream out) throws IOException {
 	int nLength = name.length;
 	out.writeInt(nLength);
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/DiskIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/DiskIndex.java
index d5e7eb6..d5ba2b9 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/DiskIndex.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/DiskIndex.java
@@ -19,6 +19,7 @@
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.search.*;
 import org.eclipse.jdt.internal.core.util.*;
+import org.eclipse.osgi.util.NLS;
 import org.eclipse.jdt.internal.compiler.util.HashtableOfIntValues;
 import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
 import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
@@ -753,6 +754,10 @@
 	for (int i = 1; i < size; i++) {
 		if (stream != null && this.bufferIndex + 2 >= this.bufferEnd)
 			readStreamBuffer(stream);
+		// bug 566262: check if the index file was deleted in parallel, if so throw an IOException instead of risking to run into index OOB exceptions
+		if (this.bufferEnd == -1 && stream.available() == 0) {
+			throw new IOException(NLS.bind("Stream was closed for index location \"{0}\"", this.indexLocation)); //$NON-NLS-1$
+		}
 		int start = this.streamBuffer[this.bufferIndex++] & 0xFF;
 		int end = this.streamBuffer[this.bufferIndex++] & 0xFF;
 		String next  = new String(readStreamChars(stream));
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/Index.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/Index.java
index 93cb3e7..11c9edd 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/Index.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/index/Index.java
@@ -32,7 +32,7 @@
 public class Index {
 
 public String containerPath;
-public ReadWriteMonitor monitor;
+public volatile ReadWriteMonitor monitor;
 
 // Separator to use after the container path
 static final char DEFAULT_SEPARATOR = '/';
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
index 41efb7e..37666c7 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
@@ -593,8 +593,7 @@
 
 		// check if the same request is not already in the queue
 		IndexRequest request = new IndexAllProject(project, this);
-		if (!isJobWaiting(request))
-			request(request);
+		requestIfNotWaiting(request);
 	} finally {
 		// Enable index manager after adding all new index requests to the queue.
 		enable();
@@ -652,8 +651,7 @@
 	}
 
 	// check if the same request is not already in the queue
-	if (!isJobWaiting(request))
-		request(request);
+	requestIfNotWaiting(request);
 }
 
 synchronized boolean addIndex(IPath containerPath, IndexLocation indexFile) {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexRequest.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexRequest.java
index 3391fec..0ffda9d 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexRequest.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexRequest.java
@@ -17,7 +17,7 @@
 import org.eclipse.jdt.internal.core.search.processing.IJob;
 
 public abstract class IndexRequest implements IJob {
-	protected boolean isCancelled = false;
+	protected volatile boolean isCancelled;
 	protected IPath containerPath;
 	protected IndexManager manager;
 
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
index 59177b5..dbbaaf7 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java
@@ -13,6 +13,11 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Platform;
@@ -31,6 +36,7 @@
 import org.eclipse.jdt.internal.core.ClasspathEntry;
 import org.eclipse.jdt.internal.core.JavaModel;
 import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jdt.internal.core.NameLookup;
 import org.eclipse.jdt.internal.core.PackageFragmentRoot;
 import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
 import org.eclipse.jdt.internal.core.nd.IReader;
@@ -45,11 +51,7 @@
 import org.eclipse.jdt.internal.core.nd.java.model.IndexBinaryType;
 import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils;
 import org.eclipse.jdt.internal.core.nd.util.PathMap;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import org.eclipse.jdt.internal.core.util.Util;
 
 public class IndexBasedJavaSearchEnvironment implements INameEnvironment, SuffixConstants {
 
@@ -58,6 +60,8 @@
 	private IPackageFragmentRoot[] roots;
 	private int sourceEntryPosition;
 	private List<ClasspathLocation> unindexedEntries = new ArrayList<>();
+	private long timeSpentInFindClassInUnindexedLocations;
+	private long timeSpentInFindType;
 
 	public IndexBasedJavaSearchEnvironment(List<IJavaProject> javaProject, org.eclipse.jdt.core.ICompilationUnit[] copies) {
 		this.workingCopies = JavaSearchNameEnvironment.getWorkingCopyMap(copies);
@@ -127,41 +131,49 @@
 			bestEntryPosition = this.sourceEntryPosition;
 		}
 
-		char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(binaryName);
-		JavaIndex index = JavaIndex.getIndex();
-		Nd nd = index.getNd();
-		try (IReader lock = nd.acquireReadLock()) {
-			NdTypeId typeId = index.findType(fieldDescriptor);
+		long start = -1;
+		if (NameLookup.VERBOSE)
+			start = System.currentTimeMillis();
+		try {
+			char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(binaryName);
+			JavaIndex index = JavaIndex.getIndex();
+			Nd nd = index.getNd();
+			try (IReader lock = nd.acquireReadLock()) {
+				NdTypeId typeId = index.findType(fieldDescriptor);
 
-			if (typeId != null) {
-				List<NdType> types = typeId.getTypes();
-				for (NdType next : types) {
-					NdResourceFile resource = next.getFile();
+				if (typeId != null) {
+					List<NdType> types = typeId.getTypes();
+					for (NdType next : types) {
+						NdResourceFile resource = next.getFile();
 
-					IPath path = resource.getPath();
-					Integer nextRoot = this.mapPathsToRoots.getMostSpecific(path);
-					if (nextRoot != null) {
-						IPackageFragmentRoot root = this.roots[nextRoot];
+						IPath path = resource.getPath();
+						Integer nextRoot = this.mapPathsToRoots.getMostSpecific(path);
+						if (nextRoot != null) {
+							IPackageFragmentRoot root = this.roots[nextRoot];
 
-						ClasspathEntry classpathEntry = (ClasspathEntry)root.getRawClasspathEntry();
-						AccessRuleSet ruleSet = classpathEntry.getAccessRuleSet();
-						AccessRestriction accessRestriction = ruleSet == null? null : ruleSet.getViolatedRestriction(binaryName);
-						TypeRef typeRef = TypeRef.create(next);
-						String fileName = new String(binaryName) + ".class"; //$NON-NLS-1$
-						IBinaryType binaryType = new IndexBinaryType(typeRef, fileName.toCharArray());
-						NameEnvironmentAnswer nextAnswer = new NameEnvironmentAnswer(binaryType, accessRestriction);
+							ClasspathEntry classpathEntry = (ClasspathEntry)root.getRawClasspathEntry();
+							AccessRuleSet ruleSet = classpathEntry.getAccessRuleSet();
+							AccessRestriction accessRestriction = ruleSet == null? null : ruleSet.getViolatedRestriction(binaryName);
+							TypeRef typeRef = TypeRef.create(next);
+							String fileName = new String(binaryName) + ".class"; //$NON-NLS-1$
+							IBinaryType binaryType = new IndexBinaryType(typeRef, fileName.toCharArray());
+							NameEnvironmentAnswer nextAnswer = new NameEnvironmentAnswer(binaryType, accessRestriction);
 
-						boolean useNewAnswer = isBetter(result, bestEntryPosition, nextAnswer, nextRoot);
+							boolean useNewAnswer = isBetter(result, bestEntryPosition, nextAnswer, nextRoot);
 
-						if (useNewAnswer) {
-							bestEntryPosition = nextRoot;
-							result = nextAnswer;
+							if (useNewAnswer) {
+								bestEntryPosition = nextRoot;
+								result = nextAnswer;
+							}
 						}
 					}
 				}
+			} catch (JavaModelException e) {
+				// project doesn't exist
 			}
-		} catch (JavaModelException e) {
-			// project doesn't exist
+		} finally {
+			if (NameLookup.VERBOSE)
+				this.timeSpentInFindType += System.currentTimeMillis()-start;
 		}
 
 		return result;
@@ -171,71 +183,79 @@
 	 * Search unindexed locations on the classpath for the given class
 	 */
 	private NameEnvironmentAnswer findClassInUnindexedLocations(String qualifiedTypeName, char[] typeName) {
-		String
-			binaryFileName = null, qBinaryFileName = null,
-			sourceFileName = null, qSourceFileName = null,
-			qPackageName = null;
-		NameEnvironmentAnswer suggestedAnswer = null;
-		Iterator <ClasspathLocation> iter = this.unindexedEntries.iterator();
-		while (iter.hasNext()) {
-			ClasspathLocation location = iter.next();
-			NameEnvironmentAnswer answer;
-			if (location instanceof ClasspathSourceDirectory) {
-				if (sourceFileName == null) {
-					qSourceFileName = qualifiedTypeName; // doesn't include the file extension
-					sourceFileName = qSourceFileName;
-					qPackageName =  ""; //$NON-NLS-1$
-					if (qualifiedTypeName.length() > typeName.length) {
-						int typeNameStart = qSourceFileName.length() - typeName.length;
-						qPackageName =  qSourceFileName.substring(0, typeNameStart - 1);
-						sourceFileName = qSourceFileName.substring(typeNameStart);
+		long start = -1;
+		if (NameLookup.VERBOSE)
+			start = System.currentTimeMillis();
+		try {
+			String
+				binaryFileName = null, qBinaryFileName = null,
+				sourceFileName = null, qSourceFileName = null,
+				qPackageName = null;
+			NameEnvironmentAnswer suggestedAnswer = null;
+			Iterator <ClasspathLocation> iter = this.unindexedEntries.iterator();
+			while (iter.hasNext()) {
+				ClasspathLocation location = iter.next();
+				NameEnvironmentAnswer answer;
+				if (location instanceof ClasspathSourceDirectory) {
+					if (sourceFileName == null) {
+						qSourceFileName = qualifiedTypeName; // doesn't include the file extension
+						sourceFileName = qSourceFileName;
+						qPackageName =  ""; //$NON-NLS-1$
+						if (qualifiedTypeName.length() > typeName.length) {
+							int typeNameStart = qSourceFileName.length() - typeName.length;
+							qPackageName =  qSourceFileName.substring(0, typeNameStart - 1);
+							sourceFileName = qSourceFileName.substring(typeNameStart);
+						}
 					}
-				}
-				org.eclipse.jdt.internal.compiler.env.ICompilationUnit workingCopy = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) this.workingCopies.get(qualifiedTypeName);
-				if (workingCopy != null) {
-					answer = new NameEnvironmentAnswer(workingCopy, null /*no access restriction*/);
+					org.eclipse.jdt.internal.compiler.env.ICompilationUnit workingCopy = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) this.workingCopies.get(qualifiedTypeName);
+					if (workingCopy != null) {
+						answer = new NameEnvironmentAnswer(workingCopy, null /*no access restriction*/);
+					} else {
+						answer = location.findClass(
+							sourceFileName, // doesn't include the file extension
+							qPackageName,
+							null, // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
+							qSourceFileName,  // doesn't include the file extension
+							false,
+							null /*no module filtering on source dir*/);
+					}
 				} else {
-					answer = location.findClass(
-						sourceFileName, // doesn't include the file extension
-						qPackageName,
-						null, // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
-						qSourceFileName,  // doesn't include the file extension
-						false,
-						null /*no module filtering on source dir*/);
-				}
-			} else {
-				if (binaryFileName == null) {
-					qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class;
-					binaryFileName = qBinaryFileName;
-					qPackageName =  ""; //$NON-NLS-1$
-					if (qualifiedTypeName.length() > typeName.length) {
-						int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
-						qPackageName =  qBinaryFileName.substring(0, typeNameStart - 1);
-						binaryFileName = qBinaryFileName.substring(typeNameStart);
+					if (binaryFileName == null) {
+						qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class;
+						binaryFileName = qBinaryFileName;
+						qPackageName =  ""; //$NON-NLS-1$
+						if (qualifiedTypeName.length() > typeName.length) {
+							int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
+							qPackageName =  qBinaryFileName.substring(0, typeNameStart - 1);
+							binaryFileName = qBinaryFileName.substring(typeNameStart);
+						}
 					}
+					answer =
+						location.findClass(
+							binaryFileName,
+							qPackageName,
+							null,  // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
+							qBinaryFileName,
+							false,
+							null /*no module filtering, this env is not module aware*/);
 				}
-				answer =
-					location.findClass(
-						binaryFileName,
-						qPackageName,
-						null,  // TODO(SHMOD): don't have a module name, but while looking in unindexed classpath locations, this is probably OK
-						qBinaryFileName,
-						false,
-						null /*no module filtering, this env is not module aware*/);
+				if (answer != null) {
+					if (!answer.ignoreIfBetter()) {
+						if (answer.isBetter(suggestedAnswer))
+							return answer;
+					} else if (answer.isBetter(suggestedAnswer))
+						// remember suggestion and keep looking
+						suggestedAnswer = answer;
+				}
 			}
-			if (answer != null) {
-				if (!answer.ignoreIfBetter()) {
-					if (answer.isBetter(suggestedAnswer))
-						return answer;
-				} else if (answer.isBetter(suggestedAnswer))
-					// remember suggestion and keep looking
-					suggestedAnswer = answer;
-			}
+			if (suggestedAnswer != null)
+				// no better answer was found
+				return suggestedAnswer;
+			return null;
+		} finally {
+			if (NameLookup.VERBOSE)
+				this.timeSpentInFindClassInUnindexedLocations += System.currentTimeMillis()-start;
 		}
-		if (suggestedAnswer != null)
-			// no better answer was found
-			return suggestedAnswer;
-		return null;
 	}
 
 	public boolean isBetter(NameEnvironmentAnswer currentBest, int currentBestClasspathPosition,
@@ -324,6 +344,15 @@
 		// No explicit cleanup required for this class
 	}
 
+	public void printTimeSpent() {
+		if(!NameLookup.VERBOSE)
+			return;
+
+		Util.verbose(" TIME SPENT IndexBasedJavaSearchEnvironment");  //$NON-NLS-1$
+		Util.verbose(" -> findClassInUnindexedLocations..." +  this.timeSpentInFindClassInUnindexedLocations + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+		Util.verbose(" -> findType........................" +  this.timeSpentInFindType + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
 	public static INameEnvironment create(List<IJavaProject> javaProjects, org.eclipse.jdt.core.ICompilationUnit[] copies) {
 		if (JavaIndex.isEnabled() && isEnabled()) {
 			return new IndexBasedJavaSearchEnvironment(javaProjects, copies);
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java
index 30fd398..a80c55a 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java
@@ -15,20 +15,24 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.core.search.matching;
 
+import static java.util.stream.Collectors.joining;
+
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.IModuleDescription;
 import org.eclipse.jdt.core.IPackageDeclaration;
+import org.eclipse.jdt.core.IPackageFragment;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
@@ -57,11 +61,13 @@
  */
 public class JavaSearchNameEnvironment implements IModuleAwareNameEnvironment, SuffixConstants {
 
-	LinkedHashSet<ClasspathLocation> locationSet;
+	protected /* visible for testing only */ LinkedHashSet<ClasspathLocation> locationSet;
 	Map<String, IModuleDescription> modules;
 	private boolean modulesComputed = false;
 	Map<String,ClasspathLocation> moduleLocations;
 	Map<String,LinkedHashSet<ClasspathLocation>> moduleToClassPathLocations;
+	/** an index of qualified package names (separated by / not .) to classpath locations */
+	protected /* visible for testing only */ Map<String,LinkedHashSet<ClasspathLocation>> packageNameToClassPathLocations;
 
 	/*
 	 * A map from the fully qualified slash-separated name of the main type (String) to the working copy
@@ -74,8 +80,54 @@
 		this.moduleToClassPathLocations = new HashMap<>();
 	}
 	this.modules = new HashMap<>();
+	this.packageNameToClassPathLocations = new HashMap<>();
+
+	long start = 0;
+	if (NameLookup.VERBOSE) {
+		Util.verbose(" BUILDING JavaSearchNameEnvironment");  //$NON-NLS-1$
+		Util.verbose(" -> project: " + javaProject);  //$NON-NLS-1$
+		Util.verbose(" -> working copy size: " + (copies == null ? 0 : copies.length));  //$NON-NLS-1$
+		start = System.currentTimeMillis();
+	}
+
 	this.locationSet = computeClasspathLocations((JavaProject) javaProject);
 	this.workingCopies = getWorkingCopyMap(copies);
+
+	// if there are working copies, we need to index their packages too
+	if(this.workingCopies.size() > 0) {
+		Optional<ClasspathLocation> firstSrcLocation = this.locationSet.stream().filter(cp -> cp instanceof ClasspathSourceDirectory).findFirst();
+		if(!firstSrcLocation.isPresent()) {
+			/*
+			 * Specifying working copies but not providing a project with a source folder is not supported by the current implementation.
+			 * I'm not sure if this is valid use case, though.
+			 *
+			 * However, there is one test that (potentially) relies on this behavior. At lease it expects this constructor to NOT fail.
+			 *
+			 * org.eclipse.jdt.core.tests.model.ClassFileTests.testWorkingCopy11()
+			 */
+			//throw new IllegalArgumentException("Missing source folder for searching working copies: " + javaProject); //$NON-NLS-1$
+		    if (NameLookup.VERBOSE) {
+				Util.verbose(" -> ignoring working copies; no ClasspathSourceDirectory on project classpath ");  //$NON-NLS-1$
+		    }
+		} else {
+			for (String qualifiedMainTypeName : this.workingCopies.keySet()) {
+				int typeNameStart = qualifiedMainTypeName.lastIndexOf('/');
+				if(typeNameStart > 0) {
+					String pkgName = qualifiedMainTypeName.substring(0, typeNameStart);
+					addPackageNameToIndex(firstSrcLocation.get(), pkgName);
+				} else {
+					addPackageNameToIndex(firstSrcLocation.get(), IPackageFragment.DEFAULT_PACKAGE_NAME);
+				}
+			}
+		}
+	}
+
+
+    if (NameLookup.VERBOSE) {
+		Util.verbose(" -> pkg roots size: " + (this.locationSet == null ? 0 : this.locationSet.size()));  //$NON-NLS-1$
+		Util.verbose(" -> pkgs size: " + this.packageNameToClassPathLocations.size());  //$NON-NLS-1$
+        Util.verbose(" -> spent: " + (System.currentTimeMillis() - start) + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+    }
 }
 
 public static Map<String, org.eclipse.jdt.core.ICompilationUnit> getWorkingCopyMap(
@@ -104,11 +156,25 @@
 @Override
 public void cleanup() {
 	this.locationSet.clear();
+	this.packageNameToClassPathLocations.clear();
 }
 
-void addProjectClassPath(JavaProject javaProject) {
+protected /* visible for testing only */ void addProjectClassPath(JavaProject javaProject) {
+	long start = 0;
+	if (NameLookup.VERBOSE) {
+		Util.verbose(" EXTENDING JavaSearchNameEnvironment");  //$NON-NLS-1$
+		Util.verbose(" -> project: " + javaProject);  //$NON-NLS-1$
+		start = System.currentTimeMillis();
+	}
+
 	LinkedHashSet<ClasspathLocation> locations = computeClasspathLocations(javaProject);
 	if (locations != null) this.locationSet.addAll(locations);
+
+    if (NameLookup.VERBOSE) {
+		Util.verbose(" -> pkg roots size: " + (this.locationSet == null ? 0 : this.locationSet.size()));  //$NON-NLS-1$
+		Util.verbose(" -> pkgs size: " + this.packageNameToClassPathLocations.size());  //$NON-NLS-1$
+        Util.verbose(" -> spent: " + (System.currentTimeMillis() - start) + "ms");  //$NON-NLS-1$ //$NON-NLS-2$
+    }
 }
 
 private LinkedHashSet<ClasspathLocation> computeClasspathLocations(JavaProject javaProject) {
@@ -131,11 +197,47 @@
 	JavaModelManager manager = JavaModelManager.getJavaModelManager();
 	for (int i = 0; i < length; i++) {
 		ClasspathLocation cp = mapToClassPathLocation(manager, (PackageFragmentRoot) roots[i], imd);
-		if (cp != null) locations.add(cp);
+		if (cp != null) {
+			try {
+				indexPackageNames(cp, roots[i]);
+				locations.add(cp);
+			} catch (JavaModelException e) {
+				Util.log(e, "Error indexing package names!"); //$NON-NLS-1$
+			}
+		}
 	}
 	return locations;
 }
 
+private void indexPackageNames(ClasspathLocation cp, IPackageFragmentRoot root) throws JavaModelException {
+	for (IJavaElement c : root.getChildren()) {
+		String qualifiedPackageName = c.getElementName().replace('.', '/');
+		addPackageNameToIndex(cp, qualifiedPackageName);
+	}
+	/* In theory IPackageFragmentRoot#getChildren should contain all. It always returns
+	 * the default package (no matter what). However, for some reason only JarPackageFragmentRoot#getChildren
+	 * really returns all children. PackageFragmentRoot#getChildren returns ONLY the default package for binary class folders.
+	 *
+	 * We therefore also go through listPackages as well
+	 */
+	char[][] packages = cp.listPackages();
+	if(packages != null) {
+		for (char[] packageName : packages) {
+			String qualifiedPackageName = CharOperation.charToString(packageName).replace('.', '/');
+			addPackageNameToIndex(cp, qualifiedPackageName);
+		}
+	}
+
+}
+
+private void addPackageNameToIndex(ClasspathLocation cp, String qualifiedPackageName) {
+	LinkedHashSet<ClasspathLocation> cpl = this.packageNameToClassPathLocations.get(qualifiedPackageName);
+	if(cpl == null) {
+		this.packageNameToClassPathLocations.put(qualifiedPackageName, cpl = new LinkedHashSet<>());
+	}
+	cpl.add(cp);
+}
+
 private void computeModules() {
 	if (!this.modulesComputed) {
 		this.modulesComputed = true;
@@ -229,12 +331,20 @@
 private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName, LookupStrategy strategy, /*@Nullable*/String moduleName) {
 	String
 		binaryFileName = null, qBinaryFileName = null,
-		sourceFileName = null, qSourceFileName = null,
-		qPackageName = null;
+		sourceFileName = null, qSourceFileName = null;
+
+	final String qPackageName;
+	final int typeNameStart;
+	if (qualifiedTypeName.length() > typeName.length) {
+		typeNameStart = qualifiedTypeName.length() - typeName.length;
+		qPackageName =  qualifiedTypeName.substring(0, typeNameStart - 1);
+	} else {
+		typeNameStart = 0;
+		qPackageName =  ""; //$NON-NLS-1$
+	}
+
 	NameEnvironmentAnswer suggestedAnswer = null;
-	Iterator<ClasspathLocation> iter = getLocationsFor(moduleName);
-	while (iter.hasNext()) {
-		ClasspathLocation location = iter.next();
+	for (ClasspathLocation location : getLocationsFor(moduleName, qPackageName)) {
 		if (!strategy.matches(location, ClasspathLocation::hasModule))
 			continue;
 		NameEnvironmentAnswer answer;
@@ -242,10 +352,7 @@
 			if (sourceFileName == null) {
 				qSourceFileName = qualifiedTypeName; // doesn't include the file extension
 				sourceFileName = qSourceFileName;
-				qPackageName =  ""; //$NON-NLS-1$
-				if (qualifiedTypeName.length() > typeName.length) {
-					int typeNameStart = qSourceFileName.length() - typeName.length;
-					qPackageName =  qSourceFileName.substring(0, typeNameStart - 1);
+				if (typeNameStart > 0) {
 					sourceFileName = qSourceFileName.substring(typeNameStart);
 				}
 			}
@@ -265,10 +372,7 @@
 			if (binaryFileName == null) {
 				qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class;
 				binaryFileName = qBinaryFileName;
-				qPackageName =  ""; //$NON-NLS-1$
-				if (qualifiedTypeName.length() > typeName.length) {
-					int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
-					qPackageName =  qBinaryFileName.substring(0, typeNameStart - 1);
+				if (typeNameStart > 0) {
 					binaryFileName = qBinaryFileName.substring(typeNameStart);
 				}
 			}
@@ -283,26 +387,59 @@
 		}
 		if (answer != null) {
 			if (!answer.ignoreIfBetter()) {
-				if (answer.isBetter(suggestedAnswer))
+				if (answer.isBetter(suggestedAnswer)) {
+					if(NameLookup.VERBOSE) {
+						Util.verbose(" Result for JavaSearchNameEnvironment#findClass( " + qualifiedTypeName + ", " + CharOperation.charToString(typeName) + ", " + strategy + ", " + moduleName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+						Util.verbose(" -> answer: " + answer); //$NON-NLS-1$
+						Util.verbose(" -> location: " + location); //$NON-NLS-1$
+					}
 					return answer;
-			} else if (answer.isBetter(suggestedAnswer))
+				}
+			} else if (answer.isBetter(suggestedAnswer)) {
 				// remember suggestion and keep looking
 				suggestedAnswer = answer;
+				if(NameLookup.VERBOSE) {
+					Util.verbose(" Potential answer for JavaSearchNameEnvironment#findClass( " + qualifiedTypeName + ", " + CharOperation.charToString(typeName) + ", " + strategy + ", " + moduleName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+					Util.verbose(" -> answer: " + answer); //$NON-NLS-1$
+					Util.verbose(" -> location: " + location); //$NON-NLS-1$
+				}
+			}
 		}
 	}
 	if (suggestedAnswer != null)
 		// no better answer was found
 		return suggestedAnswer;
+	if(NameLookup.VERBOSE) {
+		Util.verbose(" NO result for JavaSearchNameEnvironment#findClass( " + qualifiedTypeName + ", " + CharOperation.charToString(typeName) + ", " + strategy + ", " + moduleName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+	}
 	return null;
 }
 
-private Iterator<ClasspathLocation> getLocationsFor(/*@Nullable*/String moduleName) {
+protected /* visible for testing only */ Iterable<ClasspathLocation> getLocationsFor(/*@Nullable*/String moduleName, String qualifiedPackageName) {
 	if (moduleName != null) {
 		LinkedHashSet<ClasspathLocation> l = this.moduleToClassPathLocations.get(moduleName);
-		if (l != null && l.size() > 0)
-			return l.iterator();
+		if (l != null)
+			return l;
+		// FIXME: this seems bogus ... if we are searching with a module name and there is NONE, an empty set should be returned, shouldn't it?
 	}
-	return this.locationSet.iterator();
+	if(qualifiedPackageName != null) {
+		LinkedHashSet<ClasspathLocation> cpls = this.packageNameToClassPathLocations.get(qualifiedPackageName);
+		if(cpls == null) {
+			if(NameLookup.VERBOSE) {
+				Util.verbose(" No result for JavaSearchNameEnvironment#getLocationsFor( " + moduleName + ", " + qualifiedPackageName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			}
+			return Collections.emptySet();
+		}
+		if(NameLookup.VERBOSE) {
+			Util.verbose(" Result for JavaSearchNameEnvironment#getLocationsFor( " + moduleName + ", " + qualifiedPackageName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			Util.verbose(" -> " + cpls.stream().map(Object::toString).collect(joining(" | "))); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		return cpls;
+	}
+	if(NameLookup.VERBOSE) {
+		Util.verbose(" Potentially expensive search in JavaSearchNameEnvironment#getLocationsFor( " + moduleName + ", " + qualifiedPackageName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	}
+	return this.locationSet;
 }
 
 @Override
@@ -335,9 +472,8 @@
 		if (this.moduleToClassPathLocations != null) {
 			String moduleNameString = String.valueOf(moduleName);
 			LinkedHashSet<ClasspathLocation> cpl = this.moduleToClassPathLocations.get(moduleNameString);
-			List<ClasspathLocation> l = cpl != null ? cpl.stream().collect(Collectors.toList()): null;
-			if (l != null) {
-				for (ClasspathLocation cp : l) {
+			if (cpl != null) {
+				for (ClasspathLocation cp : cpl) {
 					if (cp.isPackage(qualifiedPackageName, moduleNameString))
 						return new char[][] { moduleName };
 				}
@@ -346,7 +482,7 @@
 		return null;
 	}
 	char[][] moduleNames = CharOperation.NO_CHAR_CHAR;
-	for (ClasspathLocation location : this.locationSet) {
+	for (ClasspathLocation location : getLocationsFor(null /* ignore module */, qualifiedPackageName)) {
 		if (strategy.matches(location, ClasspathLocation::hasModule) ) {
 			if (location.isPackage(qualifiedPackageName, null)) {
 				char[][] mNames = location.getModulesDeclaringPackage(qualifiedPackageName, null);
@@ -355,6 +491,10 @@
 			}
 		}
 	}
+	if(NameLookup.VERBOSE) {
+		Util.verbose(" Result for JavaSearchNameEnvironment#getModulesDeclaringPackage( " + qualifiedPackageName + ", " + CharOperation.charToString(moduleName) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		Util.verbose(" -> " + CharOperation.toString(moduleNames)); //$NON-NLS-1$
+	}
 	return moduleNames == CharOperation.NO_CHAR_CHAR ? null : moduleNames;
 }
 
@@ -386,10 +526,15 @@
 				return location.hasCompilationUnit(qualifiedPackageNameString, moduleNameString);
 		}
 	} else {
-		for (ClasspathLocation location : this.locationSet) {
+		for (ClasspathLocation location : getLocationsFor(null /* ignore module */, qualifiedPackageNameString)) {
 			if (strategy.matches(location, ClasspathLocation::hasModule) )
-				if (location.hasCompilationUnit(qualifiedPackageNameString, moduleNameString))
+				if (location.hasCompilationUnit(qualifiedPackageNameString, moduleNameString)) {
+					if(NameLookup.VERBOSE) {
+						Util.verbose(" Result for JavaSearchNameEnvironment#hasCompilationUnit( " + qualifiedPackageNameString + ", " + moduleNameString + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						Util.verbose(" -> " + location); //$NON-NLS-1$
+					}
 					return true;
+				}
 		}
 	}
 	return false;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
index 8c436b4..95eaf0d 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
@@ -1215,7 +1215,7 @@
 	projects.add(project);
 	if (this.pattern.focus != null) {
 		IJavaProject focusProject = this.pattern.focus.getJavaProject();
-		if (focusProject != project) {
+		if (!project.equals(focusProject)) {
 			projects.add(focusProject);
 		}
 	}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
index 1ccc768..20dd9e4 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java
@@ -24,10 +24,10 @@
 	protected IJob[] awaitingJobs = new IJob[10];
 	protected int jobStart = 0;
 	protected int jobEnd = -1;
-	protected boolean executing = false;
+	protected volatile boolean executing;
 
 	/* background processing */
-	protected Thread processingThread;
+	protected volatile Thread processingThread;
 	protected Job progressJob;
 
 	/* counter indicating whether job execution is enabled or not, disabled if <= 0
@@ -272,6 +272,19 @@
 	}
 	public abstract String processName();
 
+	/**
+	 * Schedules given job for execution is there is no equal jobs waiting in the queue already
+	 *
+	 * @see JobManager#isJobWaiting(IJob)
+	 * @param job
+	 *            a job to schedule (or not)
+	 */
+	public synchronized void requestIfNotWaiting(IJob job) {
+		if(!isJobWaiting(job)) {
+			request(job);
+		}
+	}
+
 	public synchronized void request(IJob job) {
 
 		job.ensureReadyToRun();